我创建了一个在单独进程中运行的服务
<service
android:name="dashboard.main.InterfaceService"
android:exported="false"
android:process=":ServiceProcess" >
</service>
我读过的时候,服务是作为单独的进程进行的,它是在不在主线程上的单独线程上。当我在服务中执行HTTP请求时,我得到例外:Thread forbids Http
。
主线程和服务线程的线程信息都是相同的主要:(id)1:(priority)5:(group)main
。
服务在started/bind()
中为MainActivity
。
两者之间的相互作用是使用AIDL。
连接服务后MainActivity保存服务返回的对象的引用,并使用此对象进一步调用服务方法。
在DDMS视图中,可以看到两个单独的进程请帮忙,我需要让服务在自己的线程上运行
------------新信息 正如“acj”建议的那样 - 我在所有课程中都进行了PID检查。 由于两个进程都有不同的堆,因此通过AIDL从我的服务返回的对象在活动堆中成为另一个副本。所以每次我使用这个对象来启动Http Request时,我都在使用Activity Process Heap中的对象,即它在主线程上。
我的推理是否合适?
答案 0 :(得分:3)
不要查找线程信息,而是打印出进程和/或线程ID,以确认服务和应用程序是否在单独的线程/进程中运行。
android.os.Process.myPid()/android.os.Process.myTid()
我最好的猜测是Android中的每个进程默认都有一个主线程,这可能是因为你看到了相同的信息。此外,如果这是真的,那么主线程永远不允许进行任何网络调用,这也是运行服务的进程的应用程序,并且您必须使用一些AsyncTask或为此创建新线程。希望它有所帮助。
答案 1 :(得分:0)
来自AIDL docs:
默认情况下, RPC调用是同步的。如果你知道该服务需要几毫秒才能完成一个请求,你不应该从活动的主线程中调用它,因为它可能会挂起应用程序(Android可能会显示“应用程序没有响应”对话框) - 你应该通常从客户端的单独线程中调用它们。
当您在IBinder
实例上调用方法来执行HTTP请求时,调用线程(在本例中,可能是UI线程)阻塞并触发异常。要利用服务的后台线程,您应该通过Intent
发送工作。
根据您的要求,IntentService
或AsyncTask
可能是更好的方法。
如果您使用IntentService
(即使在相同的应用程序进程中),Intent
将在后台线程上处理。您需要一种机制将结果发送回客户端(在本例中为Activity
);我为此目的使用了BroadcastReceiver
。
如果您的服务仅发出简短的HTTP请求并将结果返回到Activity
,则AsyncTask
将更容易实现。
修改强>:
我认为Android处理线程的方式存在一些困惑。您的体系结构的问题在于您在IBinder
对象上调用了一个长时间运行的方法,并且您正在从应用程序的UI线程中调用它。当方法运行时,这种类型的RPC调用将阻止,并将导致您所描述的异常。
将长时间运行的工作发送到服务的推荐方法是Intent
。正如我之前所说,IntentService
将自动处理后台线程中的意图,并且不需要单独的进程。 (通常只有在服务可能崩溃并且您不希望它关闭应用程序时才需要单独的进程。)
答案 2 :(得分:0)
bindService()在服务的进程中创建一个binderThread(创建一个新的MainThread),该线程属于“主”组。因此,从技术上讲,AIDL存根接口内部的方法是在不同的进程上运行,但在主线程上运行。要解决此问题,您可以使用IntentService来启动工作线程,但是如果必须从AIDL方法返回该值,则可以在服务中使用HnadlerThread并使用回调来完成。下面是示例
MyAIDLInterface.aidl
import com.example.todokotlin.ResultCallback;
interface IMyAidlInterface {
void add(int a, int b,ResultCallback callback);
}
ResultCallback.aidl
interface ResultCallback {
void onResult(int result);
}
MyService.kt
class MyService: Service() {
lateinit var handlerThread:HandlerThread
override fun onCreate() {
super.onCreate()
handlerThread = HandlerThread("MyService",Process.THREAD_PRIORITY_BACKGROUND)
handlerThread.start()
}
override fun onBind(intent: Intent?): IBinder? {
return object:IMyAidlInterface.Stub(){
override fun add(a: Int, b: Int,callback: ResultCallback){
Handler(handlerThread.looper).post{
Thread.sleep(60000) // you can do you HTTP call hear
callback.onResult(a+b)
}
}
}
}
override fun onUnbind(intent: Intent?): Boolean {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2)
handlerThread.looper.quitSafely()
else
handlerThread.looper.quit()
return super.onUnbind(intent)
}
override fun onDestroy() {
super.onDestroy()
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2)
handlerThread.looper.quitSafely()
else
handlerThread.looper.quit()
}
}
用于调用远程接口,在设置通常的serviceConnection和bindingService之后,从客户端传递ResultCallback
ClientActivity.kt
class MainActivity : AppCompatActivity() {
private var mService: IMyAidlInterface? = null
private val mConn:ServiceConnection = object: ServiceConnection{
override fun onServiceDisconnected(name: ComponentName?) {
mService = null
}
override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
mService = IMyAidlInterface.Stub.asInterface(service)
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
startButton.setOnClickListener {
mService?.add(5,5,object : ResultCallback.Stub() {
override fun onResult(result: Int) {
runOnUiThread {
Toast.makeText(applicationContext,result.toString(), Toast.LENGTH_LONG).show()
}
}
})
}
}
override fun onResume() {
super.onResume()
val i = Intent()
i.component = ComponentName(packageName,packageName+"MyService")
bindService(i,mConn, Context.BIND_AUTO_CREATE)
}
override fun onPause() {
super.onPause()
unbindService(mConn)
}
}
上述方法还可以帮助您排队多个客户请求。