Android Work Manager中的新Android Places SDK

问题描述 投票:1回答:2

作为前言,我正在使用react-native,我们的应用程序使用了大量的后台进程。我们已经尝试了许多基于本机的反应解决方案,例如后台获取,后台地理定位等。我们在测试中发现,在后台线程中收集数据的最佳方法是Android的本机工作管理器。我已经设法在工作管理器中实现了一些基本的东西,如时间戳和应用程序使用。但现在我正在努力获取新的Android Places SDK(Google商家信息现已弃用)以便在Work Manager中使用。下面是我得到的错误的图片。

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

所以在遇到任何错误之后,我自然而然地提到了Async任务,并创建了一个处理程序。由于对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);

    }



}

有没有人试图在Work Manager中实现Places SDK?如果已经接受权限,为什么Places SDK必须与主UI线程通信?任何意见是极大的赞赏。我已经查看了Looper和AsyncTask的文档,但两者都没有多大意义,因为我没有任何上下文可以将它们放入。如果你建议请提供关于在何处使用它的上下文。

android google-places-api google-places android-workmanager
2个回答
0
投票

高级:您在Worker中调用异步API。工作者用于同步后台执行。您可能希望查看ListenableWorker,RxWorker或CoroutineWorker:https://developer.android.com/topic/libraries/architecture/workmanager/advanced/threading

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


0
投票

经过多次努力,我已经找到了一些问题。我会分享我的代码并解释(据我所知)我做错了什么以及我是如何为那些可能尝试做类似事情的人解决的。

首先,我的位置代码!

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的上下文和活动传递给它并且它工作了!

接下来是我的可听工人/线程课程!

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,它允许您处理自己的线程并能够运行异步任务。我拿出了一些代码来使它更通用,所以它可能有点奇怪但是在测试之后我能够在工作人员开火时将google地方响应记录到logcat!我确实有一些小问题可以解决,但似乎工作得很好!

如果可以进一步改进,或者您有更深入的见解并且可以更好地解释,请告诉我。

© www.soinside.com 2019 - 2024. All rights reserved.