Я разрабатывал решение, в котором я пытался реализовать «длинный опрос» для моей системы уведомлений в службе, которая выполняет проверку внутри потока и работоспособна (я пробовал и другие решения, такие как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: процесс также не решает эту проблему