AsyncTasks на исполнителе заблокирован фоновым потоком, работающим - PullRequest
0 голосов
/ 29 сентября 2019

Я разрабатывал решение, в котором я пытался реализовать «длинный опрос» для моей системы уведомлений в службе, которая выполняет проверку внутри потока и работоспособна (я пробовал и другие решения, такие какTimerTask и Handler)

Я "новичок", поэтому, если мой пост отстой, вы ненавидите мой код, и я вам не нравлюсь, это нормально, но, пожалуйста, отвечайте, только если у вас есть конструктивные вещи, чтобы сказать(так много моих постов были «атакованы» «профи»).

Я дам вам краткое изложение приложения - у меня есть вход в систему, который использует новый AsyncTask.execute () для проведенияего вход в систему с моим соединителем php.У меня есть класс RequestHandler, который содержит функции httpurlconnection.После входа в систему действие входа в систему начинает новое намерение, открывающее основное действие моих приложений - действие ViewPager без фрагментов и несколько макетов xmls для задания содержимого элементов пейджера (в настоящее время в пейджере четыре страницы).

Когда пейджер разбит на страницы, я выполняю AsyncTasks (ранее используя execute, а теперь использую executeOnExecutor), чтобы публиковать в PHP API своего коннектора, чтобы содержимое этой страницы обновлялось (я строю большинство представлений динамически с помощью обратных вызовов onPostExecute длябыли вызваны AsyncTasks). Все это работает волшебным образом, мой пейджер имеет минимальную задержку при нормальной работе и обновляет, как и ожидалось.

Поэтому я реализовал обычную службу (я также попробовал 'IntentService') дляпериодически проверять мой коннектор PHP для уведомлений. На интервале, когда коннектор PHP отвечает немедленно, все это прекрасно работает (с использованием TimerTask и Thread с Runnable) для получения обновлений и push-уведомлений в Broadcast Receiver.

Затем я подумал: «Я должен использовать длинные опросы с длинными таймаутами в моих проверках уведомлений и вместо этого проводить опрос на стороне PHP на время ожидания тайм-аута».Я хочу сделать это, чтобы: а) снизить частоту опроса на стороне моего приложения для Android (сократить использование сетевых данных) и б) потому что, когда приходит уведомление с использованием "длинного опроса", оно почти сразу же отправляется в мое приложение (я бы предпочелреализовать решение с помощью WebSockets, но мой хост не поддерживает их).Эта техника «длинного опроса» прекрасно работает, за исключением одной вещи:

Неважно, что я делаю. Мои ASYNCTASKS ПРОСМОТРА ПРОСМОТРЕВАЮТ, КОГДА У СТОРОНЫ СЕРВИСА ОТКРЫТОЕ СОЕДИНЕНИЕ И ОЖИДАНИЕ.т. е. мой пейджер не обновляется в пользовательском интерфейсе, когда служба активно ожидает ответа, и я не могу понять, почему!

Сервисный код (несколько отредактирован):

   public class NotifierService extends Service {
        private String thisgID;
        private String thissessID;
        private String thisgDatID;
        private String thisgFirstName;
        private String thisgLastName;
        private String isSiteMgr;
        private String sMgrId;
        private String thiscID;
        public int counter = 0;
    Thread updateThread;


        public NotifierService(Context context) {

        }
    public NotifierService(){
            super();
    }

        @Override
        public int onStartCommand(Intent intent, int flags, int startId) {
            super.onStartCommand(intent, flags, startId);
            final Bundle extras = intent.getExtras();
            if (extras == null) {
                //cID=null;
                //sessID=null;
                //cDatID=null;
            } else {
                thisgID = extras.getString("GID");
                thissessID = extras.getString("SESSID");
                thisgDatID = extras.getString("GDATID");
            }
            updateThread = new Thread(updateRunnable);
            updateThread.setPriority(10);
            updateThread.start();

           sendCustomNotification(getApplicationContext(),"MyApp","THIS BODY");
            //pushServiceNotify(getApplicationContext(), "MyApp", "App service is running in the background");

            return START_STICKY;
        }
        private Handler mHandler = new Handler();

        private int lifeSignDelay = 5000;


        public void sendCustomNotification(Context context, String title, String body) {
            NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
            NotificationCompat.Builder mBuilder;
            Notification notification;
            RemoteViews contentView;
            String versionName;
            PackageManager manager = context.getPackageManager();
            try {
                PackageInfo info = manager.getPackageInfo(context.getPackageName(), 0);
                versionName = info.versionName;

            } catch (PackageManager.NameNotFoundException e) {
                e.printStackTrace();
                versionName = "";
            }
            //private static NotificationManager notificationManager=new NotificationManager;
            int NotificationID = 1005;
            mBuilder = new NotificationCompat.Builder(context, "mynotify");

            contentView = new RemoteViews(getPackageName(), R.layout.activity_lg__mainnotify);
            contentView.setImageViewResource(R.id.image, R.mipmap.ic_launcher);
            contentView.setTextViewText(R.id.versioninfo,"v"+versionName);
            final Intent intent = getPackageManager().getLaunchIntentForPackage(getPackageName()).setPackage(null).setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);


            PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, 0);
            //PendingIntent pendingSwitchIntent = PendingIntent.getBroadcast(context, 1020, switchIntent, 0);
            //contentView.setOnClickPendingIntent(R.id.flashButton, pendingSwitchIntent);
            mBuilder.setSmallIcon(R.drawable.ic_notify);
            mBuilder.setAutoCancel(false);
            mBuilder.setOngoing(true);
            mBuilder.setPriority(Notification.PRIORITY_HIGH);
            mBuilder.setOnlyAlertOnce(true);
            mBuilder.build().flags = Notification.FLAG_NO_CLEAR | Notification.PRIORITY_HIGH;
            mBuilder.setContent(contentView);
            mBuilder.setContentIntent(pendingIntent);

            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                String channelId = "lgguard";
                NotificationChannel channel = new NotificationChannel(channelId, "Lion Guard", NotificationManager.IMPORTANCE_HIGH);
                channel.enableVibration(true);
                channel.setVibrationPattern(new long[]{100, 200, 300, 400, 500, 400, 300, 200, 400});
                notificationManager.createNotificationChannel(channel);
                mBuilder.setChannelId(channelId);
            }

            notification = mBuilder.build();
            notificationManager.notify(NotificationID, notification);

        }
        @Override
        public void onDestroy() {
            super.onDestroy();

            Log.i("EXIT", "ondestroy!");
            Intent broadcastIntent = new Intent(this, NotifierRestarterBroadcastReceiver.class);
            broadcastIntent.setAction("com.myapp.myapp-client.myservice_restart");
            sendBroadcast(broadcastIntent);

        }



        @Nullable
        @Override
        public IBinder onBind(Intent intent) {
            return null;
        }

         Runnable updateRunnable = new Runnable(){
            public void run(){
                String ls = null;
                JSONObject jResponse_getupdate = null;
                int timeadd = 0;
               // Log.i("SRVTMR", "in timer getupdate ++++  " + (counter++));
                // DO A LONG POLL OPERATION ON NOTIFICATIONS UPDATE REQUEST
                String supdate = null;
                while(true) {
                    try {

                        JSONObject postDataParams = new JSONObject();
                        postDataParams.put("sessID", thissessID);
                        postDataParams.put("gID", thisgID);
                        postDataParams.put("cID", sMgrId);
                        URL serviceurl = new URL("http://myapp.xyz/notifier?act=getupdate");
                        Log.d("SENDPOST", "in notifier send post ");
                        HttpURLConnection serviceconn = (HttpURLConnection) serviceurl.openConnection();
                        serviceconn.setReadTimeout(20000);
                        serviceconn.setConnectTimeout(3000);
                        serviceconn.setRequestMethod("POST");
                        serviceconn.setRequestProperty("User-Agent", "Mozilla/5.0 (Myconomii; MYAPP-CLIENT; en-US; rv:1.0.1a)");
                        serviceconn.setDoInput(true);
                        serviceconn.setDoOutput(true);


                        OutputStream serviceos = serviceconn.getOutputStream();
                        BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(serviceos, "UTF-8"));
                        writer.write(encodeParams(postDataParams));
                        writer.close();
                        serviceos.close();

                        int responseCode = serviceconn.getResponseCode(); // To Check for 200
                        if (responseCode == HttpsURLConnection.HTTP_OK) {

                            BufferedReader servicein = new BufferedReader(new InputStreamReader(serviceconn.getInputStream()));
                            StringBuffer servicesb = new StringBuffer("");
                            String line = ""
                            while ((line = servicein.readLine()) != null) {
                                servicesb.append(line);
                                break;
                            }
                            servicein.close();
                            supdate = servicesb.toString();
                            //supdate =  sendPost("http://mysite.xyz/notifier?act=getupdate", postDataParams);
                        }
                        }catch(Exception e){

                        }

                        try{
                        if (supdate != null) {

                   //         Log.i("SRVTMR", "POSTRESULT: " + supdate);

                            jResponse_getupdate = new JSONObject(supdate);
                            ls = jResponse_getupdate.getString("ls");


                            if (ls.equalsIgnoreCase("AUTH_OK")) {
    // DOING MY PARSING HERE AND broadcasting to receiver
                             }
                        } else {
                             }  

                    } catch (Exception e) {
                        Log.d("SERVICEGETUPDATE", e.toString());
                    }
                    try{
                    Thread.sleep(25000);}catch(Exception e){}
                }

            }

        };

    }

Класс AsyncTask 'RequestHandler', используемый моими активинами пользовательского интерфейса, выглядит следующим образом:

public class RequestHandler {
    private static final String LINE_FEED = "\r\n";
    public static String sendPost(String r_url , JSONObject postDataParams) throws Exception {
        URL url = new URL(r_url);

        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
        conn.setReadTimeout(20000);
        conn.setConnectTimeout(20000);
        conn.setRequestMethod("POST");
        conn.setRequestProperty("User-Agent", "Mozilla/5.0 (Myconomii; MY-APP; en-US; rv:1.0.1a)");
        conn.setAllowUserInteraction(true);
        conn.setDoInput(true);
        conn.setDoOutput(true);


        OutputStream os = conn.getOutputStream();
        BufferedWriter writer = new BufferedWriter( new OutputStreamWriter(os, "UTF-8"));
        writer.write(encodeParams(postDataParams));
        writer.close();
        os.close();

        int responseCode=conn.getResponseCode(); // To Check for 200
        if (responseCode == HttpsURLConnection.HTTP_OK) {

            BufferedReader in=new BufferedReader( new InputStreamReader(conn.getInputStream()));
            StringBuffer sb = new StringBuffer("");
            String line="";
            while((line = in.readLine()) != null) {
                sb.append(line);
                break;
            }
            in.close();
            return sb.toString();
        }

        return null;
    }
 private static String encodeParams(JSONObject params) throws Exception {
        StringBuilder result = new StringBuilder();
        boolean first = true;
        Iterator<String> itr = params.keys();
        while(itr.hasNext()){
            String key= itr.next();
            Object value = params.get(key);
            if (first)
                first = false;
            else
                result.append("&");

            result.append(URLEncoder.encode(key, "UTF-8"));
            result.append("=");
            result.append(URLEncoder.encode(value.toString(), "UTF-8"));
        }
        return result.toString();
    }
}

В своей активности ViewPager я вызываю AsyncTasks так:

new GetCalendarShifts().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);

doInBackgroundвыглядят так:

 public class GetCalendarShifts extends AsyncTask<String, String, String> {
        @Override
        protected String doInBackground(String... strings) {
            try {
                Calendar calander = Calendar.getInstance();
                SimpleDateFormat simpleDateFormat = new SimpleDateFormat("HH");

                int currhour = Integer.parseInt(simpleDateFormat.format(calander.getTime()));

                JSONObject postDataParams = new JSONObject();
                postDataParams.put("sessID", sessID);
                postDataParams.put("gID", gID);

                if(currhour > 4){
                    postDataParams.put("date",getTodayDateString());
                }else{
                    postDataParams.put("date", getYesterdayDateString());
                }
                //postDataParams.put("date","2019-08-01");

                return RequestHandler.sendPost("http://myapp.xyz/schedule?", postDataParams);
            } catch (Exception e) {

                Log.d("GETCALENDAR", e.toString());
                return new String("Exception: " + e.getMessage());
            }

        }

И я анализирую ответ JSON в onPostExecute (s)

Итак, любые идеи относительно того, почему мои запросы ViewPagers AsyncTask будут ждатьдля открытого httpurlconnection в моем сервисном потоке runnable?

ОБНОВЛЕНИЕ: я попытался принудительно перевести мой сервис в новый поток и с помощью Runnable (в своем собственном потоке?) получить обновление, и мое приложение продолжает блокироваться надлинный опрос ..

   @Override
    public int onStartCommand(final Intent intent, final int flags, final int startId) {

        Thread t = new Thread("NotifierService(" + startId + ")") {

            @Override
            public void run() {
                _onStart(intent, flags, startId);
               stopSelf();
            }
        };
        t.start();
    return START_STICKY;
    }

Я поднял свои элементы onStartCommand на _onStart (это все из другого поста, чтобы получить ваш сервис в собственной ветке)

Я начинаю думать, что этопроблема с HttpURLConnection (повторное использование сокетов?) или BufferedReader ..

ОБНОВЛЕНИЕ 2: с тех пор я пытался использовать okhttp 4.2.0, и он имеет ту же проблему - мои асинктические задачи делают его в фоновом режиме.Это новая okhttp-версия моей задачи «doinbackground» sendPost.Я также реализовал аналогичный метод в своих подпрограммах 'getUpdate' для службы.

 public static String sendPostOkHttp(String r_url , JSONObject postDataParams) throws IOException {
        ConnectionPool conPool=new ConnectionPool(10,10,TimeUnit.SECONDS);

        try {

            OkHttpClient client = new OkHttpClient.Builder().connectionPool(conPool).connectTimeout(20,TimeUnit.SECONDS).writeTimeout(20,TimeUnit.SECONDS).readTimeout(20,TimeUnit.SECONDS).build();
            FormBody.Builder formBuilder = new FormBody.Builder();
            Iterator<String> itr = postDataParams.keys();
            try {
                while (itr.hasNext()) {
                    String key = itr.next();
                    Object value = postDataParams.get(key);
                    formBuilder.add(key,value.toString());
                }
            }catch(JSONException e){}
            RequestBody body = formBuilder.build();
            Log.d("OKHTTP",postDataParams.toString());
            Request request = new Request.Builder()
                    .url(r_url)
                    .post(body)
                    .build();
            Log.d("OKHTTP","executing request is the last instruction before the background task hangs");
            Response response = client.newCall(request).execute();
            Log.d("OKHTTP","request is excecuted as soon as the service update finishes if this task hasn't timed out by the end of that event");
           // Log.d("OKHTTP",response.body().string());
            return response.body().string();

        }catch(IOException e){

            Log.d("OKHTTP",e.toString());
        }
        return null;
    }

ОБНОВЛЕНИЕ 3: создание службы самостоятельно с помощью манифестов android: процесс также не решает эту проблему

...