由於公司項目涉及到sip通話這塊,所以嘗試了pjsip庫 android自帶的sip庫和linphone庫 自帶的sip庫掛斷各種回調在測試大多是不回調的。 pjsip庫好用回調也準確,但是在公司對接的線路中,可以撥打但是沒有聲音,如果是接聽其它端打過來的則有聲音。好像是線路路由方面的問題,所以最後只能放棄該框架。最後選擇了linphone這個庫 引入遠程庫包 implementation "org.linphone:linphone-sdk-android:4.2-182-g1eb2091"
新建服務LinphoneService
public class LinphoneService extends Service { private static LinphoneService sInstance; private int retry=0; private int successSize=0; private int zhuceSize=0; private Handler mHandler; private Timer mTimer; private Core mCore; public CoreListenerStub mCoreListener; AccountCreator mAccountCreator; private String name="",pwd="",domain=""; Call myCall; private boolean isComing=false; private boolean isExit=false; TimerTask lTask; public static boolean isReady() { return sInstance != null; } public static LinphoneService getInstance() { return sInstance; } public static Core getCore() { return sInstance.mCore; } @Nullable @Override public IBinder onBind(Intent intent) { return null; } @Override public void onCreate() { super.onCreate(); if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){ // MyNotificationManager.getInstance().getManager().createNotificationChannel(MyNotificationManager.getInstance().getChannel()); // startForeground(1,MyNotificationManager.getInstance().getBuilder().setChannelId("1").build()); MyNotificationManager.getInstance().createService(this,10086); } String basePath = getFilesDir().getAbsolutePath(); Factory.instance().setLogCollectionPath(basePath); Factory.instance().enableLogCollection(LogCollectionState.Enabled); Factory.instance().setDebugMode(false, getString(R.string.app_name)); mHandler = new Handler(); // This will be our main Core listener, it will change activities depending on events mCoreListener = new CoreListenerStub() { @Override public void onRegistrationStateChanged(Core core, ProxyConfig cfg, RegistrationState state, String message) { if (state == RegistrationState.Ok) { // successSize++; // Toast.makeText(LinphoneService.this, "註冊成功!"+successSize + message, Toast.LENGTH_SHORT).show(); MyLogUtils.testLog("state==註冊成功"); } else if (state == RegistrationState.Failed) { MyLogUtils.testLog("state==註冊失敗"); // retry++; // new Handler().postDelayed(new Runnable() { // @Override // public void run() { // init(name,pwd,domain); // } // },retry*10000); } } @Override public void onCallStateChanged(Core core, Call call, Call.State state, String message) { System.out.println("state=="+state.name()+" message=="+message+"call"+call.getRemoteAddressAsString()); if (state == Call.State.IncomingReceived) {//來電 // if(isComing) {return;} isComing=true; myCall=call; // if(!App.isInToGroupCall){ // Intent intent = new Intent(LinphoneService.this, CallActivity.class); // intent.putExtra("type","0"); // intent.putExtra("user",call.getRemoteAddressAsString()); // intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); // startActivity(intent); // } // CallParams params = getCore().createCallParams(call); // params.enableVideo(false); // call.acceptWithParams(params); } else if (state == Call.State.Connected) { isComing=true; // This stats means the call has been established, let's start the call activity // Toast.makeText(LinphoneService.this, "接通", Toast.LENGTH_SHORT).show(); }else if (state == Call.State.End) { // Toast.makeText(LinphoneService.this, "通話結束", Toast.LENGTH_SHORT).show(); // if(!App.isInToGroupCall){ // EventBus.getDefault().post(new CallEvent()); // } }else if(state == Call.State.Released){ isComing=false; // Toast.makeText(LinphoneService.this, "就緒", Toast.LENGTH_SHORT).show(); } else if(state== Call.State.StreamsRunning){ isComing=true; // Toast.makeText(LinphoneService.this, "通話中", Toast.LENGTH_SHORT).show(); } } }; try { copyIfNotExist(R.raw.linphonerc_default, basePath + "/.linphonerc"); copyFromPackage(R.raw.linphonerc_factory, "linphonerc"); } catch (IOException ioe) { Log.e(ioe); } try { configureCore(basePath); } catch (Exception e) { } } public void jieting(){//接聽電話 if (mCore==null){ MyToastUtils.showToast("接聽失敗!"); return; } CallParams params = mCore.createCallParams(myCall); params.enableVideo(false); myCall.acceptWithParams(params); } public void init(String name,String pwd,String domain){//註冊賬號 // if(isExit) { // String basePath = getFilesDir().getAbsolutePath(); // try { // copyIfNotExist(R.raw.linphonerc_default, basePath + "/.linphonerc"); // copyFromPackage(R.raw.linphonerc_factory, "linphonerc"); // } catch (IOException ioe) { // Log.e(ioe); // } // try { // configureCore(basePath); // } catch (Exception e) { // // } // mCore.start();} this.name=name; this.pwd=pwd; this.domain=domain; if(TextUtils.isEmpty(this.name)||TextUtils.isEmpty(this.pwd)||TextUtils.isEmpty(this.domain)){ return; } if(mCore==null){ new Handler().postDelayed(new Runnable() { @Override public void run() { init(name,pwd,domain); } },500); return; } // zhuceSize++; // Toast.makeText(this, "開始註冊"+zhuceSize, Toast.LENGTH_SHORT).show(); isExit=false; mAccountCreator=mCore.createAccountCreator(null); mAccountCreator.setUsername(name); mAccountCreator.setDomain(domain); mAccountCreator.setPassword(pwd); mAccountCreator.setTransport(TransportType.Udp); // mAccountCreator.setTransport(TransportType.Tls); ProxyConfig cfg = mAccountCreator.createProxyConfig(); cfg.setExpires(1); mCore.setDefaultProxyConfig(cfg); } public void logOut(){//登出賬號 if (mAccountCreator==null) return; // if(!isExit) { // mCore.stop(); // mCore = null; // } isExit=true; ProxyConfig cfg = mAccountCreator.createProxyConfig(); cfg.setExpires(0);//註銷 mCore.setDefaultProxyConfig(cfg); // mCore.clearCallLogs(); mCore.removeProxyConfig(cfg); // mAccountCreator=null; // mCore.clearProxyConfig(); // mCore.clearAllAuthInfo(); } @Override public int onStartCommand(Intent intent, int flags, int startId) { super.onStartCommand(intent, flags, startId); // If our Service is already running, no need to continue if (sInstance != null) { return START_STICKY; } // Our Service has been started, we can keep our reference on it // From now one the Launcher will be able to call onServiceReady() sInstance = this; // Core must be started after being created and configured // mCore.start();//這句會導致服務一啓動就註冊。 // We also MUST call the iterate() method of the Core on a regular basis TimerTask lTask = new TimerTask() { @Override public void run() { mHandler.post( new Runnable() { @Override public void run() { if (isExit){ return; } if (mCore != null) { try { mCore.iterate(); }catch (Exception e){ } } } }); } }; mTimer = new Timer("Linphone scheduler"); mTimer.schedule(lTask, 0, 20); return START_STICKY; } @Override public void onDestroy() { mCore.removeListener(mCoreListener); mTimer.cancel(); mCore.stop(); // A stopped Core can be started again // To ensure resources are freed, we must ensure it will be garbage collected mCore = null; // Don't forget to free the singleton as well sInstance = null; super.onDestroy(); } @Override public void onTaskRemoved(Intent rootIntent) { // For this sample we will kill the Service at the same time we kill the app stopSelf(); super.onTaskRemoved(rootIntent); } private void configureCore(String basePath) { mCore = Factory.instance() .createCore(basePath + "/.linphonerc", basePath + "/linphonerc", this); mCore.enableEchoCancellation(true);//回聲消除 mCore.enableAdaptiveRateControl(true);//自適應碼率控制 mCore.addListener(mCoreListener); App.isAddCallListen=true; // We will create a directory for user signed certificates if needed String userCerts = basePath + "/user-certs"; File f = new File(userCerts); if (!f.exists()) { if (!f.mkdir()) { Log.e(userCerts + " can't be created."); } } mCore.setUserCertificatesPath(userCerts); } private void dumpDeviceInformation() { StringBuilder sb = new StringBuilder(); sb.append("DEVICE=").append(Build.DEVICE).append("\n"); sb.append("MODEL=").append(Build.MODEL).append("\n"); sb.append("MANUFACTURER=").append(Build.MANUFACTURER).append("\n"); sb.append("SDK=").append(Build.VERSION.SDK_INT).append("\n"); sb.append("Supported ABIs="); for (String abi : Version.getCpuAbis()) { sb.append(abi).append(", "); } sb.append("\n"); Log.i(sb.toString()); } private void dumpInstalledLinphoneInformation() { PackageInfo info = null; try { info = getPackageManager().getPackageInfo(getPackageName(), 0); } catch (PackageManager.NameNotFoundException nnfe) { Log.e(nnfe); } if (info != null) { Log.i( "[Service] Linphone version is ", info.versionName + " (" + info.versionCode + ")"); } else { Log.i("[Service] Linphone version is unknown"); } } private void copyIfNotExist(int ressourceId, String target) throws IOException { File lFileToCopy = new File(target); if (!lFileToCopy.exists()) { copyFromPackage(ressourceId, lFileToCopy.getName()); } } private void copyFromPackage(int ressourceId, String target) throws IOException { FileOutputStream lOutputStream = openFileOutput(target, 0); InputStream lInputStream = getResources().openRawResource(ressourceId); int readByte; byte[] buff = new byte[8048]; while ((readByte = lInputStream.read(buff)) != -1) { lOutputStream.write(buff, 0, readByte); } lOutputStream.flush(); lOutputStream.close(); lInputStream.close(); } }
//撥打電話
address格式爲 坐席號碼@域名 如[email protected]
public static void call(String address){ Core core = LinphoneService.getCore(); if (core==null||TextUtils.isEmpty(address)){ Toast.makeText(context, "撥打失敗!", Toast.LENGTH_SHORT).show(); return; } Address addressToCall = core.interpretUrl(address); CallParams params = core.createCallParams(null); params.enableVideo(false); if (addressToCall != null) { core.inviteAddressWithParams(addressToCall, params); } }
//掛斷電話
public static void guaduan(){ Core core = LinphoneService.getCore(); if(core==null) { MyToastUtils.showToast("掛斷失敗!"); return; } if (core.getCallsNb() > 0) { Call call = core.getCurrentCall(); if (call == null) { // Current call can be null if paused for example call = core.getCalls()[0]; } call.terminate(); } }
//項目中遇到一個問題就是註冊到線上之後如何註銷賬號發現linphone庫並沒有提供api,所以查詢了一下網上的資料發現要發送指令Expires=0所以寫了如下語句。
ProxyConfig cfg = mAccountCreator.createProxyConfig();
cfg.setExpires(0);//註銷
測試發現在APP沒有銷燬的情況下調用註銷確實可以登出話機不再收到通話。
不過將APP殺死重新啓動 發現sip自動註冊了。
這樣就導致會突然收到sip電話的響鈴,但是並無界面,需求是在指定頁面才能進行通話,這樣肯定不符合需求。
發現是LinphoneService中的
mCore.start();//這句會導致服務一啓動就註冊。
所以將這句註釋。問題解決。
退出界面的時候就調用LinphoneService的logout方法即可,在撥打界面註冊時先調用mCore.start();再進行init註冊即可。