【问题标题】:New Android Places SDK in Android Work ManagerAndroid Work Manager 中的新 Android Places SDK
【发布时间】:2019-03-27 00:15:25
【问题描述】:

在前言中,我正在使用 react-native,我们的应用程序使用了很多后台进程。我们尝试了许多基于 React Native 的解决方案,例如 background-fetch、background-geolocation 等。我们在测试中发现,在后台线程中收集数据的最佳方法是 Android 的本机 Work Manager。我已经设法在工作管理器中实现了一些基本的东西,比如时间戳和应用程序的使用。但现在我正在努力让新的 Android Places SDK(Google Places 现已弃用)在 Work Manager 中工作。下面是我遇到的错误的图片。

Can't create handler inside thread that has not called Looper.prepare()

在遇到任何错误后,我很自然地来到这里,你们中的许多人建议异步任务,并创建一个处理程序。由于对 Android 线程处理不熟悉,我不确定如何或在哪里实现这些东西。到目前为止,我已经尝试在 onCreate() 以及我的 Worker 中调用 Looper.prepare()。

这是我的地方代码

package com.bettertime.betterLocation;

import android.Manifest;
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.support.annotation.RequiresPermission;
import android.util.Log;

import com.google.android.gms.common.api.Api;
import com.google.android.gms.common.api.ApiException;
import com.google.android.gms.tasks.Task;
import com.google.android.libraries.places.api.Places;
import com.google.android.libraries.places.api.model.Place;
import com.google.android.libraries.places.api.model.PlaceLikelihood;
import com.google.android.libraries.places.api.net.FindCurrentPlaceRequest;
import com.google.android.libraries.places.api.net.FindCurrentPlaceResponse;
import com.google.android.libraries.places.api.net.PlacesClient;


import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import android.support.v7.app.AppCompatActivity;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;

import static android.Manifest.permission.ACCESS_FINE_LOCATION;
import static android.Manifest.permission.ACCESS_WIFI_STATE;
import static com.bettertime.MainActivity.placesClient;


public class BetterLocation extends AppCompatActivity {

    private List<Place.Field> placeList = new ArrayList<>();
    private static String TAG = "Location: ";
    public static Map<String, Object> places = new HashMap<>();

    int PERMISSION_ALL = 1;
    String[] PERMISSIONS = {
            Manifest.permission.ACCESS_FINE_LOCATION,
            Manifest.permission.ACCESS_WIFI_STATE
    };



    public void findCurrentPlace() {
        places.clear();
        if(ContextCompat.checkSelfPermission(this, ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED &&
                ContextCompat.checkSelfPermission(this, ACCESS_WIFI_STATE) == PackageManager.PERMISSION_GRANTED) {
        findCurrentPlaceWithPermissions();
        }
    }


    @RequiresPermission(allOf = {ACCESS_FINE_LOCATION, ACCESS_WIFI_STATE})
    private void findCurrentPlaceWithPermissions() {


        placeList.add(Place.Field.NAME);
        placeList.add(Place.Field.ADDRESS);
        placeList.add(Place.Field.LAT_LNG);
        placeList.add(Place.Field.TYPES);

        FindCurrentPlaceRequest currentPlaceRequest =
                FindCurrentPlaceRequest.builder(placeList).build();


            if(ContextCompat.checkSelfPermission(this, ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED &&
                    ContextCompat.checkSelfPermission(this, ACCESS_WIFI_STATE) == PackageManager.PERMISSION_GRANTED) {
                Task<FindCurrentPlaceResponse> currentPlaceTask = placesClient.findCurrentPlace(currentPlaceRequest);

                currentPlaceTask.addOnCompleteListener(task -> {
                    if (task.isSuccessful()) {
                        FindCurrentPlaceResponse response = task.getResult();
                        assert response != null;
                        for (PlaceLikelihood placeLikelihood : response.getPlaceLikelihoods()) {
                            Log.d(TAG, "findCurrentPlace: "
                                    + placeLikelihood.getPlace().getName() + "\n"
                                    + placeLikelihood.getPlace().getAddress() + "\n"
                                    + placeLikelihood.getPlace().getLatLng() + "\n"
                                    + placeLikelihood.getPlace().getTypes() + "\n"
                                    + placeLikelihood.getLikelihood());

                            PlaceObj placeObj = new PlaceObj(
                                    placeLikelihood.getPlace().getName(),
                                    placeLikelihood.getPlace().getAddress(),
                                    placeLikelihood.getPlace().getLatLng(),
                                    placeLikelihood.getPlace().getTypes(),
                                    placeLikelihood.getLikelihood());

                            places.put("place", placeObj);
                        }

                    } else {
                        Exception exception = task.getException();
                        if (exception instanceof ApiException) {
                            ApiException apiException = (ApiException) exception;
                            Log.e(TAG, "findCurrentPlaceWithPermissions: " + apiException.getStatusCode());
                        }
                    }
                });
            }
    }



    //////////////////////////
    // Helper methods below //
    //////////////////////////
    private boolean checkPermission(String permission) {
        boolean hasPermission =
                ContextCompat.checkSelfPermission(this, permission) == PackageManager.PERMISSION_GRANTED;
        if (!hasPermission) {
            ActivityCompat.requestPermissions(this, new String[]{permission}, 0);
        }
        return hasPermission;
    }

    public static boolean hasPermissions(Context context, String... permissions) {
        if (context != null && permissions != null) {
            for (String permission : permissions) {
                if (ActivityCompat.checkSelfPermission(context, permission) != PackageManager.PERMISSION_GRANTED) {
                    return false;
                }
            }
        }
        return true;
    }

}

这是我的工作经理

package com.bettertime.betterWorkManager;

import android.content.Context;
import android.content.SharedPreferences;

import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.support.annotation.NonNull;
import android.util.Log;

import com.bettertime.betterLocation.BetterLocation;
import com.bettertime.packages.NativeUsageEvents;
import com.bettertime.timePackage.NativeTime;

import com.google.firebase.firestore.FirebaseFirestore;

import java.util.Calendar;
import java.util.HashMap;
import java.util.Map;
import androidx.work.Worker;
import androidx.work.WorkerParameters;

import static com.bettertime.betterLocation.BetterLocation.places;


public class BetterWorkManager extends Worker {

    private static final String TAG = "Work Manager Firing";

    private FirebaseFirestore db = FirebaseFirestore.getInstance();
    private NativeTime nativeTime = new NativeTime();
    public static Handler mHandler;
    private BetterLocation betterLocation = new BetterLocation();

    public BetterWorkManager(@NonNull Context context, @NonNull WorkerParameters workerParams) {
        super(context, workerParams);
    }




    @NonNull
    @Override
    public Worker.Result doWork() {
        Log.d(TAG, "doWork: fired");


        userStamp();
        return Result.success();
    }


    private void userStamp(){
        Looper.prepare();
         mHandler = new Handler() {
            public void handleMessage(Message msg) {
                // process incoming messages here
                betterLocation.findCurrentPlace();
            }
        };
        Looper.loop();

        Log.d(TAG, "places test: " + places.toString());

}

我去掉了一些不必要的部分,但这就是它的要点。而且这里是我的主要活动。

package com.bettertime;

import android.os.Bundle;


import com.bettertime.betterWorkManager.BetterWorkManager;
import com.facebook.react.ReactActivity;
import com.google.android.libraries.places.api.Places;
import com.google.android.libraries.places.api.net.PlacesClient;

import java.util.concurrent.TimeUnit;
import androidx.work.ExistingPeriodicWorkPolicy;
import androidx.work.PeriodicWorkRequest;
import androidx.work.WorkManager;



public class MainActivity extends ReactActivity {

    private static final String W_TAG = "Periodic Worker";
    public static PlacesClient placesClient;

    /**
     * Returns the name of the main component registered from JavaScript.
     * This is used to schedule rendering of the component.
     */
    @Override
    protected String getMainComponentName() {
        return "BetterYou";
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);


        Places.initialize(getApplicationContext(), "THIS_IS_MY_API_KEY");
        placesClient = Places.createClient(this);
        PeriodicWorkRequest fireUploadBuilder =
                new PeriodicWorkRequest.Builder(BetterWorkManager.class, 15, TimeUnit.MINUTES).build();
         WorkManager.getInstance().enqueueUniquePeriodicWork(W_TAG, ExistingPeriodicWorkPolicy.KEEP, fireUploadBuilder);

    }



}

有没有人成功尝试在工作管理器中实施 Places SDK?如果权限已被接受,为什么 Places SDK 必须与主 UI 线程通信?任何意见是极大的赞赏。我已经查看了 Looper 和 AsyncTask 的文档,但是两者都没有多大意义,因为我几乎没有将它们放入的上下文。如果您提出任何建议,请提供有关在何处使用它的上下文。

【问题讨论】:

    标签: android google-places-api google-places android-workmanager


    【解决方案1】:

    高级:您正在 Worker 内部调用异步 API。 Worker 用于同步后台执行。您可能想研究 ListenableWorker、RxWorker 或 CoroutineWorker:https://developer.android.com/topic/libraries/architecture/workmanager/advanced/threading

    另外,我强烈建议不要在 WorkManager 提供给您的线程上调用 Looper 方法。这几乎总是会导致奇怪的意外问题。

    【讨论】:

    • 谢谢,是的,我承认我不知道该把 Looper 方法放在哪里。 '^_^ 接下来我会努力实现一个 ListenableWorker!
    【解决方案2】:

    所以经过一番努力,我发现了一些我的问题。我将分享我的代码并解释(据我所知)我做错了什么以及我是如何为那些可能试图做类似事情的人解决它的。

    首先,我的位置代码!

    import android.Manifest;
    import android.app.Activity;
    import android.content.Context;
    import android.content.pm.PackageManager;
    import android.support.annotation.RequiresPermission;
    import android.support.v4.app.ActivityCompat;
    import android.support.v4.content.ContextCompat;
    import android.util.Log;
    import com.google.android.gms.common.api.ApiException;
    import com.google.android.gms.tasks.Task;
    import com.google.android.libraries.places.api.model.Place;
    import com.google.android.libraries.places.api.model.PlaceLikelihood;
    import com.google.android.libraries.places.api.net.FindCurrentPlaceRequest;
    import com.google.android.libraries.places.api.net.FindCurrentPlaceResponse;
    import java.util.ArrayList;
    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;
    import static android.Manifest.permission.ACCESS_FINE_LOCATION;
    import static android.Manifest.permission.ACCESS_WIFI_STATE;
    import static com.bettertime.MainActivity.placesClient;
    
    
    public class BetterLocation  {
    
        private List<Place.Field> placeList = new ArrayList<>();
        private static String TAG = "Location: ";
        public static Map<String, Object> places = new HashMap<>();
        int PERMISSION_ALL = 1;
        String[] PERMISSIONS = {
                Manifest.permission.ACCESS_FINE_LOCATION,
                Manifest.permission.ACCESS_WIFI_STATE
        };
    
         public void findCurrentPlace(Context context, Activity activity) {
    
            if(ContextCompat.checkSelfPermission(context, ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED &&
                    ContextCompat.checkSelfPermission(context, ACCESS_WIFI_STATE) == PackageManager.PERMISSION_GRANTED) {
                findCurrentPlaceWithPermissions(context);
             }
          else {
            ActivityCompat.requestPermissions(activity, PERMISSIONS, PERMISSION_ALL);
        }
        }
    
    
        @RequiresPermission(allOf = {ACCESS_FINE_LOCATION, ACCESS_WIFI_STATE})
        public void findCurrentPlaceWithPermissions(Context context) {
    
            placeList.add(Place.Field.NAME);
            placeList.add(Place.Field.ADDRESS);
            placeList.add(Place.Field.LAT_LNG);
            placeList.add(Place.Field.TYPES);
    
            FindCurrentPlaceRequest currentPlaceRequest =
                    FindCurrentPlaceRequest.builder(placeList).build();
    
            if(ContextCompat.checkSelfPermission(context, ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED &&
                    ContextCompat.checkSelfPermission(context, ACCESS_WIFI_STATE) == PackageManager.PERMISSION_GRANTED) {
    
                Task<FindCurrentPlaceResponse> currentPlaceTask =
                        placesClient.findCurrentPlace(currentPlaceRequest);
    
                currentPlaceTask.addOnCompleteListener(task -> {
                    if (task.isSuccessful()) {
                        places.clear();
                        FindCurrentPlaceResponse response = task.getResult();
                        assert response != null;
                        for (PlaceLikelihood placeLikelihood : response.getPlaceLikelihoods()) {
                            Log.d(TAG, "findCurrentPlace: "
                                    + placeLikelihood.getPlace().getName() + "\n"
                                    + placeLikelihood.getPlace().getAddress() + "\n"
                                    + placeLikelihood.getPlace().getLatLng() + "\n"
                                    + placeLikelihood.getPlace().getTypes() + "\n"
                                    + placeLikelihood.getLikelihood());
    
                            PlaceObj placeObj = new PlaceObj(
                                    placeLikelihood.getPlace().getName(),
                                    placeLikelihood.getPlace().getAddress(),
                                    placeLikelihood.getPlace().getLatLng(),
                                    placeLikelihood.getPlace().getTypes(),
                                    placeLikelihood.getLikelihood());
    
                            places.put(placeObj.Name, placeObj);
                        }
    
    
                    } else {
                        Exception exception = task.getException();
                        if (exception instanceof ApiException) {
                            ApiException apiException = (ApiException) exception;
                            Log.e(TAG, "findCurrentPlaceWithPermissions: " + apiException.getStatusCode() + apiException.getLocalizedMessage());
                        }
                    }
                });
            }
        }
    
    }

    之前:我将这个类用作 AppCompatActivity,它弄乱了我传递给方法的上下文和活动对象。

    之后:我更改了获取活动和上下文的方法。我在我的 MainActivity 中对其进行了测试,将 MainActivity 的上下文和活动传递给它并且它有效!

    接下来是我的 Listenable Worker/Thread 类!

    import android.app.Activity;
    import android.content.Context;
    import android.content.SharedPreferences;
    import android.os.Looper;
    import android.support.annotation.NonNull;
    import android.util.Log;
    import com.bettertime.MainActivity;
    import com.bettertime.betterLocation.BetterLocation;
    import com.bettertime.packages.NativeUsageEvents;
    import com.bettertime.timePackage.NativeTime;
    import com.google.common.util.concurrent.ListenableFuture;
    import com.google.common.util.concurrent.SettableFuture;
    import com.google.firebase.firestore.FirebaseFirestore;
    import java.util.HashMap;
    import java.util.Map;
    import androidx.work.ListenableWorker;
    import androidx.work.WorkerParameters;
    import static com.bettertime.betterLocation.BetterLocation.places;
    
    
    class LocThread implements Runnable {
        public static LocThread sInstance;
        public static LocThread getInstance(Context context) {
            if (sInstance == null) {
                //Always pass in the Application Context
                sInstance = new LocThread(context.getApplicationContext());
            }
    
            return sInstance;
        }
    
        private Context mContext;
    
        public LocThread(Context context) {
            mContext = context;
        }
    
        private BetterLocation betterLocation = new BetterLocation();
        private Activity activity = new MainActivity().mActivity;
        private String TAG = "LocThread: ";
    
        @Override
        public void run() {
            try {
                Looper.prepare();
                betterLocation.findCurrentPlace(mContext, activity);
                Looper.loop();
            } catch (NullPointerException e) {
                Log.d(TAG, "run: " + e.getLocalizedMessage());
            }
        }
    }
    
    public class FitPlaceWorker extends ListenableWorker {
    
        private static final String TAG = "FitPlace Worker: ";
        private Thread locThread;
    
    
        public FitPlaceWorker(Context context, WorkerParameters workerParams) {
            super(context, workerParams);
        }
    
    
        @NonNull
        @Override
        public ListenableFuture<Result> startWork(){
            SettableFuture<Result> result = SettableFuture.create();
            Log.d(TAG, "doWork - FitPlace fired");
    
            fitPlaceStamp();
            
            result.set(Result.success());
            return result;
        }
    
    
        private void fitPlaceStamp(){
    
            locThread = new Thread(new LocThread(getApplicationContext()));
            locThread.start();
            //log methods
            Log.d(TAG, "placeData: " + places.toString());
           
        }
    
    
    
    }

    之前: 老实说,我对 android 线程知之甚少,所以我不确定我在问题示例中做了什么。

    之后:当我花了一些时间学习更多关于线程的知识后,我想出了如何创建自己的 Thread 类。我使用上下文包装器来获取上下文,并使用 MainActivity 的活动传递给我的方法。下一步是实现一个 ListenableWorker 而不是基本的 Worker,它允许您处理自己的线程并能够运行异步任务。我拿出了一些代码让它更通用,所以它可能有点奇怪,但在测试它之后,我能够在工人解雇时记录谷歌地方对 logcat 的响应!我确实有一些小问题要解决,但它似乎工作得很好!

    请让我知道它是否可以进一步改进,或者您是否有更深入的洞察力并可能以更好的方式进行解释。

    【讨论】:

    • 我正在使用您的代码,只是想在附近找到自动取款机。但结果的地方离我很近。我希望结果包含离我很远的 ATM。另外我想将 TYPES 限制为 ATM,以便响应速度稍快。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-05-29
    • 1970-01-01
    • 1970-01-01
    • 2022-05-02
    • 1970-01-01
    • 2012-03-25
    相关资源
    最近更新 更多