Я создаю приложение для видеовызовов для сотрудников нашей команды.
Так что после некоторого времени, проведенного в переполнении стека, я сделал фоновый сервис самостоятельно запущенным и активным для большей части приложения.
Я держал свой телефон подключенным к Android Studio довольно долго, как 3 часа, и после этого, когда я попытался позвонить пользователю. В Android Studio я мог видеть, что служба XMPP пыталась запустить действие, но не смогла, потому что приложение больше не было открыто.
При точно13:08 Я попытался позвонить, и служба попыталась инициировать класс активности вызова. оно сломалось.
Я прилагаю код моей службы XMPP, Broadcast Receiver, Manifest и пример того, как можно запустить службу Activity Call.
Служба XMPP
package com.medimetry.partner_app;
import android.app.Service;
import android.content.Intent;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.IBinder;
import android.support.annotation.Nullable;
import android.util.Log;
import com.medimetry.partner_app.utils.Class_SharedPreferences;
import com.medimetry.partner_app.webRTC.ServerPingWithAlarmManager;
import com.medimetry.partner_app.webRTC.XmppRTCClient;
import org.jivesoftware.smack.*;
import org.jivesoftware.smack.filter.AndFilter;
import org.jivesoftware.smack.packet.Stanza;
import org.jivesoftware.smack.tcp.XMPPTCPConnection;
import org.jivesoftware.smack.tcp.XMPPTCPConnectionConfiguration;
import org.jivesoftware.smackx.iqregister.AccountManager;
import org.jivesoftware.smackx.ping.PingFailedListener;
import org.jivesoftware.smackx.ping.PingManager;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import java.io.IOException;
import java.io.StringReader;
import java.util.HashMap;
import java.util.Map;
import static com.medimetry.partner_app.CallActivity.EXTRA_CAPTURETOTEXTURE_ENABLED;
import static com.medimetry.partner_app.CallActivity.EXTRA_ROOMID;
public class XMPPService extends Service {
private Intent intent;
private Class_SharedPreferences sharedPreferences;
private XmppAsyncConnection xmppAsyncConnection;
public XMPPTCPConnection conn1;
private static XMPPService self;
private XMPPEventsListener eventsListener;
public static Boolean connected = false;
public static String TAG = "xmppservice";
DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder dBuilder = null;
private String uid;
private boolean shouldSendPresence = false;
public static XMPPService instance() {
return self;
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
this.intent = intent;
return null;
}
@Override
public void onCreate() {
super.onCreate();
self = this;
sharedPreferences = new Class_SharedPreferences(this);
if (sharedPreferences.getWpUserId().equals("0")) return;
this.xmppAsyncConnection = new XmppAsyncConnection();
this.xmppAsyncConnection.execute();
XmppRTCClient.instance(null, sharedPreferences.getWpUserId()).xmppServiceInitiated();
if (dBuilder == null) {
try {
dBuilder = dbFactory.newDocumentBuilder();
} catch (ParserConfigurationException e) {
e.printStackTrace();
}
}
}
public void setEventsListener(XMPPEventsListener eventsListener) {
this.eventsListener = eventsListener;
}
public void setConnected(boolean isConnected) {
connected = isConnected;
Log.i(TAG, "IsConnected status change: " + (isConnected ? "true" : "false"));
}
public void sendPresence() {
if ( ! connected) return;
new android.os.Handler().postDelayed(
new Runnable() {
public void run() {
Log.i(TAG, "Sending presence from xmpp");
}
},
30000); // 30 seconds
}
class XmppAsyncConnection extends AsyncTask {
@Override
protected Object doInBackground(Object[] objects) {
uid = sharedPreferences.getWpUserId();
// uid = "100111";
try {
XMPPTCPConnectionConfiguration config = XMPPTCPConnectionConfiguration.builder()
.setUsernameAndPassword(uid + "@" + XmppRTCClient.EJABBERD_SERVICE_NAME, uid)
.setHost(XmppRTCClient.EJABBERD_TURN_SERVER_HOST)
.setSecurityMode(ConnectionConfiguration.SecurityMode.disabled)
.setServiceName(XmppRTCClient.EJABBERD_SERVICE_NAME)
.setDebuggerEnabled(true) // to view what's happening in detail
.setConnectTimeout(1000000)
.setCompressionEnabled(false)
.setDebuggerEnabled(true)
.setSendPresence(true)
.build();
SASLAuthentication.blacklistSASLMechanism("SCRAM-SHA-1");
SASLAuthentication.blacklistSASLMechanism("DIGEST-MD5");
SASLAuthentication.unBlacklistSASLMechanism("PLAIN");
conn1 = new XMPPTCPConnection(config);
conn1.addConnectionListener(new ConnectionListener() {
@Override
public void connected(XMPPConnection connection) {
Log.e("Connected",""+connection.getHost());
sendPresence();
setConnected(true);
}
@Override
public void authenticated(XMPPConnection connection, boolean resumed) {
Log.e("Authenticated",""+connection.getHost());
if(connection.isAuthenticated()) {
ServerPingWithAlarmManager.getInstanceFor(connection).setEnabled(true);
PingManager pingManager = PingManager.getInstanceFor(connection);
pingManager.setPingInterval(10);
try {
pingManager.pingMyServer();
pingManager.pingMyServer(true, 10);
pingManager.pingServerIfNecessary();
pingManager.registerPingFailedListener(new PingFailedListener() {
@Override
public void pingFailed() {
Log.i(TAG, "pingFailed");
conn1.disconnect();
try {
conn1.connect();
} catch (SmackException | IOException | XMPPException e) {
e.printStackTrace();
}
}
});
} catch (SmackException.NotConnectedException e) {
Log.e(TAG, e.getMessage());
}
}
}
@Override
public void connectionClosed() {
Log.e("Connection Closed","Connection Closed");
setConnected(false);
}
@Override
public void connectionClosedOnError(Exception e) {
Log.e("Connection Close Error",""+e);
// createConnection();
setConnected(false);
}
@Override
public void reconnectionSuccessful() {
Log.e("Reconnection Success","Success");
setConnected(true);
}
@Override
public void reconnectingIn(int seconds) {
setConnected(false);
}
@Override
public void reconnectionFailed(Exception e) {
Log.e("Reconnection Failed","" + e);
setConnected(false);
tryRegisteringUser(conn1, uid);
}
});
ReconnectionManager.getInstanceFor(conn1).enableAutomaticReconnection();
ReconnectionManager.setEnabledPerDefault(true);
conn1.connect();
if( conn1.isConnected() ) {
Log.w("xmpp", "conn done");
setConnected(false);
}
conn1.login();
if(conn1.isAuthenticated()) {
Log.w("xmpp", "Auth done");
}
conn1.addAsyncStanzaListener(new StanzaListener() {
@Override
public void processPacket(Stanza packet) {
Log.w("xmpp", "received packet, " + packet);
try {
Document doc = dBuilder.parse(new InputSource(new StringReader(packet.toXML().toString())));
Element element = doc.getDocumentElement();
String stanzaIdentifier = element.getTagName() + "-" + element.getAttribute("type");
if (stanzaIdentifier.equals("message-groupchat")) { // initial call request, get the app on top
if (CallActivity.active) {
CallActivity.getInstance().bringToFront();
} else {
Intent webViewAppIntent = new Intent(getApplicationContext(), CallActivity.class);
Uri uri = Uri.parse(getString(R.string.base_url));
webViewAppIntent.setData(uri);
webViewAppIntent.putExtra(EXTRA_ROOMID, sharedPreferences.getWpUserId());
webViewAppIntent.putExtra(EXTRA_CAPTURETOTEXTURE_ENABLED, true);
webViewAppIntent.putExtra("IncommingCall", true);
webViewAppIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
startActivity(webViewAppIntent);
}
}
if (eventsListener == null) {
Log.e("xmpp", "Event listener is null");
} else {
eventsListener.processStanza(doc, packet);
}
} catch (SAXException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}, new AndFilter());
eventsListener.setConnection(conn1);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
private void tryRegisteringUser(XMPPTCPConnection conn1, String uid) {
try {
if (conn1.isConnected())
{
AccountManager accountManager = AccountManager.getInstance(conn1);
Map<String, String> map = new HashMap<>();
map.put("username", uid);
map.put("name", uid);
map.put("password", uid);
map.put("email", "abc@gmail.com");
map.put("creationDate", "" + System.currentTimeMillis() / 1000L);
accountManager.createAccount(uid, uid, map);
conn1.connect();
conn1.login();
} else {
conn1.connect();
}
} catch ( Exception e) {
Log.e(TAG, "Something went wrong while registering user: " + e);
}
}
}
Приемник сетевого детектора
package com.medimetry.partner_app;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.os.CountDownTimer;
import android.util.Log;
import android.view.View;
import org.jivesoftware.smack.SmackException;
import org.jivesoftware.smack.XMPPException;
import java.io.IOException;
public class NetworkDetectorReceiver extends BroadcastReceiver {
private static final String TAG = "NetworkDetector";
Context mContext;
private boolean isNetworkConnected;
@Override
public void onReceive(Context context, Intent intent) {
Log.i(TAG, "Wokring");
mContext = context;
ConnectivityManager connectivityManager
= (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo activeNetworkInfo = connectivityManager != null ? connectivityManager.getActiveNetworkInfo() : null;
isNetworkConnected = activeNetworkInfo != null && activeNetworkInfo.isConnected();
Log.i(TAG, "Is network connected: " + isNetworkConnected);
new CountDownTimer(400, 1000) {
@Override
public void onTick(long millisUntilFinished) {
}
@Override
public void onFinish() {
if (WebViewClass.getInstance() != null) {
if (!isNetworkConnected) {
WebViewClass.getInstance().offlineRibbon.setVisibility(View.VISIBLE);
} else {
WebViewClass.getInstance().offlineRibbon.setVisibility(View.GONE);
if (WebViewClass.getInstance().errorOnStartup) {
WebViewClass.getInstance().errorOnStartup = false;
WebViewClass.getInstance().initFunction();
}
}
}
Log.e("Is Connected", "" + isNetworkConnected);
}
}.start();
// check if XMPP is connected.
Log.w(TAG, "checking if XMPP is connected: " + XMPPService.connected);
Intent serviceIntent = new Intent(mContext, XMPPService.class);
// if xmpp is connected, return
if (XMPPService.connected || !isNetworkConnected) return;
if ((XMPPService.instance() != null) && (XMPPService.instance().conn1 != null)) {
Log.i(TAG, "XMPP conn1 is actually Connected? " + XMPPService.instance().conn1.isConnected());
}
if ((XMPPService.instance() != null) && (XMPPService.instance().conn1 != null) && !XMPPService.instance().conn1.isConnected()) {
try {
XMPPService.instance().conn1.connect();
return;
} catch (SmackException | IOException | XMPPException e) {
e.printStackTrace();
}
}
mContext.stopService(serviceIntent);
mContext.startService(serviceIntent);
}
}
Файл манифеста для получения широковещания
<receiver android:name=".NetworkDetectorReceiver">
<intent-filter>
<action android:name="android.intent.action.ACTION_POWER_CONNECTED" />
<action android:name="android.intent.action.ACTION_POWER_DISCONNECTED"/>
<action android:name="android.intent.action.NEW_OUTGOING_CALL"/>
<action android:name="android.intent.action.BOOT_COMPLETED" />
<action android:name="android.intent.action.PROVIDER_CHANGED"/>
<action android:name="android.intent.action.SCREEN_ON"/>
<action android:name="android.intent.action.USER_UNLOCKED"/>
</intent-filter>
</receiver>
<service
android:name=".XMPPService"
android:enabled="true"
android:stopWithTask="false" />
и как запускается операция вызова
в службе XMPP
if (CallActivity.active) {
CallActivity.getInstance().bringToFront();
} else {
Intent incommingCallViewIntent = new Intent(getApplicationContext(), CallActivity.class);
Uri uri = Uri.parse(getString(R.string.base_url));
incommingCallViewIntent.setData(uri);
incommingCallViewIntent.putExtra(EXTRA_ROOMID, sharedPreferences.getWpUserId());
incommingCallViewIntent.putExtra(EXTRA_CAPTURETOTEXTURE_ENABLED, true);
incommingCallViewIntent.putExtra("IncommingCall", true);
incommingCallViewIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
startActivity(incommingCallViewIntent);
}
Итак, мои вопросы: 1. Верны ли мои приемники вещания, я хочу, чтобы эта служба работала каждый раз. Я переусердствовал что-нибудь или недооценил. Пожалуйста, дайте мне знать, я не уверен, как получить Wi-Fi подключить / отключить или мобильную сеть вкл / выкл действие. 2. через 2-3 часа, когда мой телефон находится в спящем режиме, при звонке я получаю эту ошибку
10-03 13:08:16.385 20956-21199/com.medimetry.partner_app E/AndroidRuntime: FATAL EXCEPTION: Smack Packet Reader (1)
Process: com.medimetry.partner_app, PID: 20956
java.lang.InternalError: Thread starting during runtime shutdown
at java.lang.Thread.nativeCreate(Native Method)
at java.lang.Thread.start(Thread.java:733)
at java.util.concurrent.ThreadPoolExecutor.addWorker(ThreadPoolExecutor.java:975)
at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1382)
at org.jivesoftware.smack.util.BoundedThreadPoolExecutor.executeBlocking(BoundedThreadPoolExecutor.java:44)
at org.jivesoftware.smack.AbstractXMPPConnection.processPacket(AbstractXMPPConnection.java:984)
at org.jivesoftware.smack.AbstractXMPPConnection.parseAndProcessStanza(AbstractXMPPConnection.java:968)
at org.jivesoftware.smack.tcp.XMPPTCPConnection.access$500(XMPPTCPConnection.java:140)
at org.jivesoftware.smack.tcp.XMPPTCPConnection$PacketReader.parsePackets(XMPPTCPConnection.java:997)
at org.jivesoftware.smack.tcp.XMPPTCPConnection$PacketReader.access$300(XMPPTCPConnection.java:952)
at org.jivesoftware.smack.tcp.XMPPTCPConnection$PacketReader$1.run(XMPPTCPConnection.java:967)
at java.lang.Thread.run(Thread.java:764)
, как также упоминается на скриншоте.
Что будет лучшим для начала активностипо запросу. Мы нацелены на надежность, такую как Google Duo.