【问题标题】:adding stopwatch to code that runs parallel to main thread in android将秒表添加到与 android 中的主线程并行运行的代码中
【发布时间】:2015-06-23 14:54:09
【问题描述】:

我正在创建一个跑步者应用程序,用户可以在其中跟踪他的跑步时间和路线。点击开始时,相机应该聚焦到当前位置,计时器文本视图应该开始更新经过的时间是秒。此外,当用户移动时,谷歌地图应开始根据用户当前位置绘制标记。

Here is the code I have written so far
package com.example.kanchan.runner7;

import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.graphics.Color;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.support.*;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v4.app.FragmentActivity;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.api.GoogleApiClient;
import com.google.android.gms.drive.Drive;
import com.google.android.gms.location.LocationServices;
import com.google.android.gms.maps.*;
import com.google.android.gms.maps.model.BitmapDescriptorFactory;
import com.google.android.gms.maps.model.CameraPosition;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.Marker;
import com.google.android.gms.maps.model.MarkerOptions;
import com.google.android.gms.maps.model.Polyline;
import com.google.android.gms.maps.model.PolylineOptions;
import com.google.android.gms.plus.Plus;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.concurrent.TimeUnit;
import java.util.logging.Handler;


//1
public class HomeScreen extends FragmentActivity//,GoogleApiClient.ConnectionCallbacks,GoogleApiClient.OnConnectionFailedListener
        implements View.OnClickListener,
        LocationListener {
    private  Button  buttonstart;
    private Button  buttonreset;
    private GoogleMap mMap;

    protected LocationManager locationManager;
    protected LocationListener locationListener;
    private List<LatLng> routePoints ;
    private Marker TP;
    private Marker TP1;
    private Polyline route1;

    private Location loc;

    private TextView tv;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        Log.d("Kimi","onCreate");
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_home_screen);

        buttonstart = (Button)findViewById(R.id.button1);
        buttonstart.setOnClickListener(this);

        buttonreset = (Button)findViewById(R.id.button2);
        buttonreset.setOnClickListener(this);

        tv = (TextView)findViewById(R.id.timer);

        setUpMapIfNeeded();

        locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
        locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, this);
        routePoints =  new ArrayList<LatLng>();
        //route1 = new Polyline();

        // TP=  new MarkerOptions().position((new LatLng(0,0)));
        loc = new Location(LocationManager.GPS_PROVIDER);

    }



    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.menu_home_screen, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();

        //noinspection SimplifiableIfStatement
        if (id == R.id.action_settings) {
            return true;
        }

        return super.onOptionsItemSelected(item);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.button1:
                buttonstartclick();
                break;



        }
    }


    private void buttonstartclick() {


        Log.d("Kimi..","kimi start button clicked");
        mMap.clear();

        mMap.animateCamera(CameraUpdateFactory.newLatLngZoom(
                new LatLng(loc.getLatitude(), loc.getLongitude()), 14));

        CameraPosition cameraPosition = new CameraPosition.Builder()
                .target(new LatLng(loc.getLatitude(), loc.getLongitude()))      // Sets the center of the map to location user
                .zoom(17)                   // Sets the zoom
                .bearing(90)                // Sets the orientation of the camera to east
                .tilt(40)                   // Sets the tilt of the camera to 30 degrees
                .build();                   // Creates a CameraPosition from the builder
        mMap.animateCamera(CameraUpdateFactory.newCameraPosition(cameraPosition));

        stopwatch s = new stopwatch();

        s.startThread();

        while (true)
        {
            int[] curTime = s.getTime();
            Log.d("Kimi result from time", Integer.toString(curTime[2]) );
            try {
                Thread.sleep(2000);
            } catch(InterruptedException e){}

            tv.setText(Integer.toString(curTime[2]));

        }



    }

    private void setUpMapIfNeeded() {
        // Do a null check to confirm that we have not already instantiated the map.
        if (mMap == null) {
            // Try to obtain the map from the SupportMapFragment.
            mMap = ((SupportMapFragment) getSupportFragmentManager().findFragmentById(R.id.mapView))
                    .getMap();
            // Check if we were successful in obtaining the map.
            if (mMap != null) {
                setUpMap();
            }
        }
    }

    private void setUpMap() {
        mMap.addMarker(new MarkerOptions().position(new LatLng(0, 0)).title("Marker"));
       /* mMap.setMapType(GoogleMap.MAP_TYPE_HYBRID);
         TP = mMap.addMarker(new MarkerOptions().
                position(new LatLng(0, 0)).title("TutorialsPoint"));*/

    }



    @Override
    public void onLocationChanged(Location location) {
        if(TP1 != null)
            TP1.remove();

        Log.d("Kimi..",Double.toString(location.getLatitude()));
        Log.d("Kimi..",Double.toString(location.getLongitude()));
        loc = location;

   /*   mMap.animateCamera(CameraUpdateFactory.newLatLngZoom(
                        new LatLng(location.getLatitude(), location.getLongitude()), 13));

                CameraPosition cameraPosition = new CameraPosition.Builder()
                .target(new LatLng(location.getLatitude(), location.getLongitude()))      // Sets the center of the map to location user
                .zoom(17)                   // Sets the zoom
                .bearing(90)                // Sets the orientation of the camera to east
                .tilt(40)                   // Sets the tilt of the camera to 30 degrees
                .build();                   // Creates a CameraPosition from the builder
            mMap.animateCamera(CameraUpdateFactory.newCameraPosition(cameraPosition));
*/
        TP = mMap.addMarker(new MarkerOptions().position(new LatLng(location.getLatitude(), location.getLongitude()))
                .icon(BitmapDescriptorFactory.fromResource(R.drawable.red2)));
        TP1 = mMap.addMarker(new MarkerOptions().position(new LatLng(location.getLatitude(), location.getLongitude())));


    }



    @Override
    public void onStatusChanged(String provider, int status, Bundle extras) {
        Log.d("Kimi","status");
    }

    @Override
    public void onProviderEnabled(String provider) {
        Log.d("Kimi","enable");
    }

    @Override
    public void onProviderDisabled(String provider) {
        Log.d("Kimi","disable");
    }
}

如果在 buttonstartclick() 中删除秒表的部分,此代码似乎工作正常(实际上是因为它是 incremantal.stopwatch 类在我的代码中扩展了线程)。相机聚焦到当前用户位置,随着用户移动,标记被放置在地图上

但是,在编写计时器代码后,相机在开始时完全停止移动。

虽然从日志中的消息我可以看到 getTime() 方法返回应该更新的正确值,但计时器文本视图不会在 while 循环内使用计时器值进行更新

while 循环似乎也没有在并行主线程中运行,因为在用户位置更改时,我看不到地图上放置的新标记。

谁能建议一种在我的代码中添加秒表功能的方法

秒表码:

public class stopwatch extends Thread
{
    private long startTime;
    private boolean started;

    public void startThread()
    {
        this.startTime = System.currentTimeMillis();
        this.started = true;
        this.start();
    }

    public void run()
    {
        while (started)
        {
            // empty code since currentTimeMillis increases by itself
        }
    }


    public int[] getTime()
    {
        long milliTime = System.currentTimeMillis() - this.startTime;
        int[] out = new int[]{0, 0, 0, 0};
        out[0] = (int)(milliTime / 3600000      );
        out[1] = (int)(milliTime / 60000        ) % 60;
        out[2] = (int)(milliTime / 1000         ) % 60;
        out[3] = (int)(milliTime)                 % 1000;

        return out;
    }

    public void stopThread()
    {
        this.started = false;
    }
}

【问题讨论】:

  • 很难说不说你的秒表代码,但 Android 有一个 Chronometer 类,它非常易于使用,可以很好地为你服务。如果可以的话,我认为你的秒表类应该以大写字母开头。
  • 添加秒表代码
  • 我认为给出的答案是正确的,除了关于创建内部类的建议。对我来说,你的案例是一个教科书案例,观察者模式将是完美的。

标签: android multithreading google-maps stopwatch


【解决方案1】:

好的,你去: (我看不到代码按钮) delTime 是经过的时间 oldTime 是开始时间 tv 是对 textview 的引用

public void run ()
{
oldTime=System.cur...;
while (running)
{
    setDeltaTime (); //computes the time went by.
    int hours=(int)(delTime/3600000);

    int minutes=(int)((delTime-hours*3600000)/60000);

    int seconds=(int)((delTime-hours*3600000-             minutes*6000)/1000);

    String s=""   +(hours <10?"0":"")
                          +hours
                          +":"
                          +(minutes<10?"0":"")
                          +minutes
                          +":"
                          +(second <10?"0":"")
                          +seconds;

   tv.setText (s);

   relax ()//give the thread some relaxation (sleep ());


}

}

所以你会以 00:00:00 格式更新文本视图,并且它已脱离 ui 线程......所以你不会阻止它

(我看到你也想要mills然后只是扩展这个例子......而且恕我直言,不需要创建一个返回数组的方法也不需要一个布尔值来检查线程是否正在运行,因为线程类有一个回调它...isAlive()...在线程启动之前设置它并不好,实际上运行时异常可能在 start() 时抛出,也许 try catch 也合适)

【讨论】:

    【解决方案2】:

    我浏览了代码,但您似乎没有实现秒表

    尽管如此,当您单击开始按钮时,您每次都会初始化一个新的整数实例 然后你调用这个不祥的启动线程方法

    然后进入一个在主线程上运行的无限循环,并在没有 if 子句的情况下基本上阻塞其他操作,顺便离开它 每次都会初始化一个新的 int 数组,用回调 gettime() 填充它

    这不应在 ui 线程中处理,而应在单独的线程中处理,可能是内部类,以便您可以轻松访问活动类的成员和方法

    您的应用应该只是冻结等待您的 while 循环结束,这就是为什么不进行绘图也不进行更新等原因

    由于您的实施,您的文本视图已更新但根本没有绘制

    【讨论】:

    • 感谢回复.. 你能解释一下如何“这不应该在 ui 线程上处理,而是在一个单独的线程中处理,可能是一个内部类,以便您可以轻松访问成员和方法你的活动课”。一些教程也可以
    • 所以首先我不会让 run 方法为空......这就是魔法应该发生的地方......我所说的魔法是指刷新文本视图加上时间的增加......我会给那个线程对象一个对文本视图的引用。 ..请记住,在写入它时确保它是非空的。我将再次为您发布一个带有代码的新答案
    • 感谢您的回复..今天试过了..它确实可以正常工作,但给了我一个好主意..最终使用 AsyncTask 实现了它
    猜你喜欢
    • 1970-01-01
    • 2022-11-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-06-22
    • 1970-01-01
    相关资源
    最近更新 更多