kotlin协程在Android中的基础应用
通过前面的三个章节,现在我们已经了解了
kotlin协程的基本使用和相关基础知识点。如:
CoroutineContext:协程上下文中包含的
Element以及下上文的作用,传递。CoroutineDispatcher:协程调度器的使用CoroutineStart:协程启动模式在不同模式下的区别CoroutineScope:协程作用域的分类,以及不同作用域下的异常处理。suspend关键字的作用,以及
Continuation的挂起恢复流程。CoroutineExceptionHandler:协程异常处理,结合
supervisorScope和
SupervisorJob的使用。这一章节中,我们将主要讲解kotlin协程在Android中的基础使用。我们先引入相关扩展库组件库:
implementation "androidx.activity:activity-ktx:1.2.2" implementation "androidx.fragment:fragment-ktx:1.3.3"复制代码
我们在之前的章节中使用协程的方式都是通过
runBlocking或者使用
GlobalScope的
launch、
async方式启动,当然也可以通过创建一个新的
CoroutineScope,然后通过
launch或者
async方式启动一个新的协程。我们在讲解
协程异常处理的篇章中就提到,通过
SupervisorJob和
CoroutineExceptionHandler实现了一个和
supervisorScope相同的作用域。
private fun testException(){
val exceptionHandler = CoroutineExceptionHandler { coroutineContext, throwable ->
Log.d("exceptionHandler", "${coroutineContext[CoroutineName].toString()} 处理异常 :$throwable")
}
val supervisorScope = CoroutineScope(SupervisorJob() + exceptionHandler)
with(supervisorScope) {
launch{
} //省略...
}
}
复制代码
在第一节中我们提到
runBlocking它会将常规的阻塞代码连接到一起,主要用于main函数和测试中。而
GlobalScope又是一个全局顶级协程,我们在之前的案例中基本都使用这种方式。但是这个协程是在整个应用程序生命周期内运行的,如果我们用
GlobalScope启动协程,我们启动一个将会变得极其繁琐,而且需要对于各种引用的处理以及管控异常取消操作。
我们可以先忽略
CoroutineExceptionHandler协程异常处理。因为不管是任何方式启动协程,如果不在程上下文中添加
CoroutineExceptionHandler,当产生未捕获的异常时都会导致应用崩溃。
那么下面代码会出现什么问题?
private fun start() {
GlobalScope.launch{
launch { //网络请求1...
throw NullPointerException("空指针")
}
val result = withContext(Dispatchers.IO) { //网络请求2...
requestData() "请求结果"
}
btn.text = result
launch { //网络请求3...
}
}
}
复制代码
因为我们的
GlobalScope默认使用的是
Dispatchers.Default,这会导致我们在非主线程上刷新UI。
子协程产生异常会产生相互干扰。子协程异常取消会导致父协程取消,同时其他子协程也将会被取消。
如果我们这个时候
activity或者
framgent退出,因为协程是在
GlobalScope中运行,所以即使
activity或者
framgent退出,这个协程还是在运行,这个时候会产生各种泄露问题。同时此协程当执行到刷新操作时,因为我们的界面已经销毁,这个时候执行UI刷新将会产生崩溃。
如果我们要解决上面的问题。我们得这么做:
var job:Job? = nullprivate fun start() {
job = GlobalScope.launch(Dispatchers.Main + SupervisorJob()) {
launch { throw NullPointerException("空指针")
}
val result = withContext(Dispatchers.IO) { //网络请求...
"请求结果"
}
launch { //网络请求3...
}
btn.text = result
}
}override fun onDestroy() { super.onDestroy()
job?.cancel()
}
复制代码
我们先需要通过
launch启动时加入
Dispatchers.Main来保证我们是在主线程刷新UI,同时还需要再
GlobalScope.launch的协程上下文中加入
SupervisorJob来避免子协程的异常取消会导致整个协程树被终结。 最后我们还得把
每次通过
GlobalScope启动的
Job保存下来,在
activity或者
framgent退出时调用
job.cancel取消整个协程树。这么来一遍感觉还行,但是我们不是写一次啊,每次写的时候会不会感觉超麻烦,甚至怀疑人生。
所以官方在kotlin协程中提供了一个默认在主线程运行的协程:
MainScope,我们可以通过它来启动协。
public fun MainScope(): CoroutineScope = ContextScope(SupervisorJob() + Dispatchers.Main) 复制代码
我们可以看到
MainScope的创建默认就使用了
SupervisorJob和
Dispatchers.Main。说明我们可以通过
MainScope来处理UI组件刷新。同时由于
MainScope采用的是
SupervisorJob,所以我们各个子协程中的异常导致的取消操作并不会导致
MainScope的取消。这就很好的简化了我们通过
GlobalScope去启动一个协程的过程。
private val mainScope = MainScope()private fun start() {
mainScope.launch {
launch { throw NullPointerException("空指针")
}
val result = withContext(Dispatchers.IO) { //网络请求...
"请求结果"
}
launch { //网络请求3...
}
btn.text = result
}
}
override fun onDestroy() { super.onDestroy()
mainScope.cancel()
}
复制代码
通过使用
MainScope我们是不是省略了很多操作。同时我们也不需要保存每一个通过
MainScope启动的
Job了,直接在最后销毁的时候调用
mainScope.cancel()就能取消所有通过
mainScope启动的协程。
这里多提一点:可能这里有的人会想,我使用
GlobalScope也不保存启动的
Job,直接
GlobalScope.cancel不行吗?如果是这样的话,那么恭喜你喜提超级崩溃BUG一个。这里就不扩展了。可以自己动手去试试,毕竟实践出真理。
那可能还有人想,我连创建
MainScope都懒得写,而且脑子经常不好使,容易忘记调用
mainScope进行
cancel操作怎么办。
官方早就为我们这些懒人想好了解决方案,这个时候我们只需要再集成一个ktx运行库就可以了。
implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.3.1"复制代码
这个时候我们就可以在
activity或者
framgent直接使用
lifecycleScope进行启动协程。我们看来看看activity中的
lifecycleScope实现
public val LifecycleOwner.lifecycleScope: LifecycleCoroutineScope get() = lifecycle.coroutineScope 复制代码
我们可以到
lifecycleScope它是通过
lifecycle得到一个
coroutineScope,是一个
LifecycleCoroutineScope对象。
public val Lifecycle.coroutineScope: LifecycleCoroutineScope get() { while (true) {
val existing = mInternalScopeRef.get() as LifecycleCoroutineScopeImpl? if (existing != null) { return existing
}
val newScope = LifecycleCoroutineScopeImpl( this,
SupervisorJob() + Dispatchers.Main.immediate
) if (mInternalScopeRef.compareAndSet(null, newScope)) {
newScope.register() return newScope
}
}
}
复制代码
我们可以看到
lifecycleScope采用的和
MainScope一样的创建
CoroutineScope,同时它又通过结合
lifecycle来实现当
lifecycle状态处于
DESTROYED状态的时候自动关闭所有的协程。
public abstract class LifecycleCoroutineScope internal constructor() : CoroutineScope {
internal abstract val lifecycle: Lifecycle public fun launchWhenCreated(block: suspend CoroutineScope.() -> Unit): Job = launch {
lifecycle.whenCreated(block)
} public fun launchWhenStarted(block: suspend CoroutineScope.() -> Unit): Job = launch {
lifecycle.whenStarted(block)
} public fun launchWhenResumed(block: suspend CoroutineScope.() -> Unit): Job = launch {
lifecycle.whenResumed(block)
}
}internal class LifecycleCoroutineScopeImpl(
override val lifecycle: Lifecycle,
override val coroutineContext: CoroutineContext
) : LifecycleCoroutineScope(), LifecycleEventObserver {
init { if (lifecycle.currentState == Lifecycle.State.DESTROYED) {
coroutineContext.cancel()
}
} fun register() {
launch(Dispatchers.Main.immediate) { if (lifecycle.currentState >= Lifecycle.State.INITIALIZED) {
lifecycle.addObserver(this@LifecycleCoroutineScopeImpl)
} else {
coroutineContext.cancel()
}
}
} override fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event) { if (lifecycle.currentState <= Lifecycle.State.DESTROYED) {
lifecycle.removeObserver(this)
coroutineContext.cancel()
}
}
}
复制代码
同时我们也可以通过
launchWhenCreated、
launchWhenStarted、
launchWhenResumed来启动协程,等到
lifecycle处于对应状态时自动触发此处创建的协程。
比如我们可以这么操作:
class MainTestActivity : AppCompatActivity() {
init {
lifecycleScope.launchWhenResumed {
Log.d("init", "在类初始化位置启动协程")
}
} override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
}
}
复制代码
D/onResume: onResume D/init: 在类初始化位置启动协程 复制代码
按照我们正常情况加载顺序,是不是应该
init先执行输出?然而在实际情况中它是在等待
Activity进入
onResume状态以后才执行接着看
launchWhenResumed中调用的
whenResumed实现。
public suspend fun <T> Lifecycle.whenResumed(block: suspend CoroutineScope.() -> T): T { return whenStateAtLeast(Lifecycle.State.RESUMED, block)
}public suspend fun <T> Lifecycle.whenStateAtLeast(
minState: Lifecycle.State,
block: suspend CoroutineScope.() -> T
): T = withContext(Dispatchers.Main.immediate) {
val job = coroutineContext[Job] ?: error("when[State] methods should have a parent job")
val dispatcher = PausingDispatcher()
val controller =
LifecycleController(this@whenStateAtLeast, minState, dispatcher.dispatchQueue, job) try {
withContext(dispatcher, block)
} finally {
controller.finish()
}
}@MainThreadinternal class LifecycleController( private val lifecycle: Lifecycle, private val minState: Lifecycle.State, private val dispatchQueue: DispatchQueue,
parentJob: Job
) { private val observer = LifecycleEventObserver { source, _ -> if (source.lifecycle.currentState == Lifecycle.State.DESTROYED) {
handleDestroy(parentJob)
} else if (source.lifecycle.currentState < minState) {
dispatchQueue.pause()
} else {
dispatchQueue.resume()
}
}
init { if (lifecycle.currentState == Lifecycle.State.DESTROYED) {
handleDestroy(parentJob)
} else {
lifecycle.addObserver(observer)
}
} private inline fun handleDestroy(parentJob: Job) {
parentJob.cancel()
finish()
} @MainThread
fun finish() {
lifecycle.removeObserver(observer)
dispatchQueue.finish()
}
}
复制代码
我们可以看到,实际上是调用了
whenStateAtLeast,同时使用了
withContext进行了一个同步操作。然后在
LifecycleController中通过添加
LifecycleObserver来监听状态,通过
lifecycle当前状态来对比我们设定的触发状态,最终决定是否恢复执行。
现在我们对于
Activity中的
lifecycleScope的创建以及销毁流程有了一个大概的了解。同理
Fragment中的
lifecycleScope实现原理也是和
Activity是一样的,这里我们就不再重复讲解。我们做个简单的实验:
class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
lifecycleScope.launch {
delay(2000)
Toast.makeText(this@MainActivity,"haha",Toast.LENGTH_SHORT).show()
}
}
}
复制代码
这个时候是不是比之前的使用方式简单多了,我们既不用关心创建过程,也不用关心销毁的过程。
这个时候我们就需要提到
CoroutineExceptionHandler协程异常处理。通过之前的章节我们知道,启动一个协程以后,如果未在协程上下文中添加
CoroutineExceptionHandler情况下,一旦产生了未捕获的异常,那么我们的程序将会崩溃退出。
class MainActivity : AppCompatActivity() {
val exceptionHandler = CoroutineExceptionHandler { coroutineContext, throwable ->
Log.d("exceptionHandler", "${coroutineContext[CoroutineName]} $throwable")
}
fun load() {
lifecycleScope.launch(exceptionHandler) { //省略...
}
lifecycleScope.launch(exceptionHandler) { //省略...
}
lifecycleScope.launch(exceptionHandler) { //省略...
}
}
}
复制代码
当出现这种情况的时候,像笔者这种有严重偷懒情结的人就开始抓狂了。为什么要写这么多遍
lifecycleScope.launch,同时每一次启动都要手动添加
CoroutineExceptionHandler。难道就不能再简便一点吗?
当然可以,首先我们自定义一个异常处理,,我们在实现上只做一个简单的异常日志输出:
/**
* @param errCode 错误码
* @param errMsg 简要错误信息
* @param report 是否需要上报
*/
class GlobalCoroutineExceptionHandler(private val errCode: Int, private val errMsg: String = "", private val report: Boolean = false) : CoroutineExceptionHandler {
override val key: CoroutineContext.Key<*>
get() = CoroutineExceptionHandler
override fun handleException(context: CoroutineContext, exception: Throwable) {
val msg = exception.stackTraceToString()
Log.e("$errCode","GlobalCoroutineExceptionHandler:${msg}")
}
}
复制代码
然后我们在通过kotlin的扩展函数来简化我们的使用,去掉重复写
lifecycleScope.launch和
exceptionHandler的过程,我们就定义三个常用方法。
inline fun AppCompatActivity.requestMain(
errCode: Int = -1, errMsg: String = "", report: Boolean = false,
noinline block: suspend CoroutineScope.() -> Unit) {
lifecycleScope.launch(GlobalCoroutineExceptionHandler(errCode, errMsg, report)) {
block.invoke(this)
}
}
inline fun AppCompatActivity.requestIO(
errCode: Int = -1, errMsg: String = "", report: Boolean = false,
noinline block: suspend CoroutineScope.() -> Unit): Job { return lifecycleScope.launch(Dispatchers.IO + GlobalCoroutineExceptionHandler(errCode, errMsg, report)) {
block.invoke(this)
}
}
inline fun AppCompatActivity.delayMain(
errCode: Int = -1, errMsg: String = "", report: Boolean = false, delayTime: Long, noinline block: suspend CoroutineScope.() -> Unit) {
lifecycleScope.launch(GlobalCoroutineExceptionHandler(errCode, errMsg, report)) {
withContext(Dispatchers.IO) {
delay(delayTime)
}
block.invoke(this)
}
}
复制代码
这个时候我们就可以愉快的在
Activity中使用了
class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
requestMain {
delay(2000)
Toast.makeText(this@MainActivity,"haha",Toast.LENGTH_SHORT).show()
}
requestIO {
loadNetData()
}
delayMain(100){
Toast.makeText(this@MainActivity,"haha",Toast.LENGTH_SHORT).show()
}
} private suspend fun loadNetData(){ //网络加载
}
}
复制代码
同样的我们再扩展一套基于
Fragment的方法
inline fun Fragment.requestMain(
errCode: Int = -1, errMsg: String = "", report: Boolean = false,
noinline block: suspend CoroutineScope.() -> Unit) {
lifecycleScope.launch(GlobalCoroutineExceptionHandler(errCode, errMsg, report)) {
block.invoke(this)
}
}
inline fun Fragment.requestIO(
errCode: Int = -1, errMsg: String = "", report: Boolean = false,
noinline block: suspend CoroutineScope.() -> Unit) {
lifecycleScope.launch(Dispatchers.IO + GlobalCoroutineExceptionHandler(errCode, errMsg, report)) {
block.invoke(this)
}
}
inline fun Fragment.delayMain(
errCode: Int = -1, errMsg: String = "", report: Boolean = false, delayTime: Long,
noinline block: suspend CoroutineScope.() -> Unit) {
lifecycleScope.launch(GlobalCoroutineExceptionHandler(errCode, errMsg, report)) {
withContext(Dispatchers.IO) {
delay(delayTime)
}
block.invoke(this)
}
}
复制代码
然后也可以愉快的在
Fragment中使用了
class HomeFragment:Fragment() {
init {
lifecycleScope.launchWhenCreated {
Toast.makeText(context,"Fragment创建了", Toast.LENGTH_SHORT).show()
}
}
override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?
): View? { return inflater.inflate(R.layout.fragment_main,container,false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState)
requestMain { //...
}
requestIO { //...
}
delayMain(100){ //...
}
}
}
复制代码
这里需要提一下,可能有的人不太明白,为什么要把
Activity和
Fragment都分开写,他们都是使用的
lifecycleScope,我们直接通过
lifecycleScope扩展就不可以了吗。假如我们这么扩展:
inline fun LifecycleCoroutineScope.requestMain(
errCode: Int = -1, errMsg: String = "", report: Boolean = false,
noinline block: suspend CoroutineScope.() -> Unit) {
launch(GlobalCoroutineExceptionHandler(errCode, errMsg, report)) {
block.invoke(this)
}
}
复制代码
我们以
Dailog为例,来启动一个协程:
val dialog = Dialog(this)
dialog.show()
(dialog.context as LifecycleOwner).lifecycleScope.requestMain {
withContext(Dispatchers.IO){ //网络加载
} // 刷新UI}
dialog.cancel()
复制代码
那么可能会出现一个什么问题?是的,内存泄露的问题以及错误的引用问题。虽然我的
dialog被销毁了,但是我们
lifecycleScope并不处于
DESTROYED状态,所以我们的协程依然会执行,这个时候我们就会出现内存泄露和崩溃问题。
通过上面的学习,我们已经基本掌握了协程在
Activity和
Fragment中的使用方式。接下来我们讲解在
Viewmodel中使用协程。
如果我们想和在
Activity和
Fragment中一样的简便、快速的在
ViewModel使用协程。那么我们就需要集成下面这个官方的
ViewModel扩展库。
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.1"复制代码
与
Activity和
Fragment不同的是,在
ViewModel我们使用的不是
lifecycleScope,而是使用
viewModelScope,使用
viewModelScope,使用
viewModelScope。重要的事情说三遍。
这里一定要注意噢,之前就有好几个人问我为什么在
viewmodel里面用不了协程,我开始纳闷半天咋就用不了呢。最后一问结果是
在
ViewModel使用
lifecycleScope,
这样做是不对滴。
public val ViewModel.viewModelScope: CoroutineScope get() {
val scope: CoroutineScope? = this.getTag(JOB_KEY) if (scope != null) { return scope
} return setTagIfAbsent(
JOB_KEY,
CloseableCoroutineScope(SupervisorJob() + Dispatchers.Main.immediate)
)
}internal class CloseableCoroutineScope(context: CoroutineContext) : Closeable, CoroutineScope {
override val coroutineContext: CoroutineContext = context
override fun close() {
coroutineContext.cancel()
}
}
复制代码
viewModelScope相比较
lifecycleScope实现会稍微简单一点。都是使用的
SupervisorJob() + Dispatchers.Main上下文,同时最终的取消操作也类似
lifecycleScope,只不过
viewModelScope取消是在
ViewModel的销毁的时候取消。
final void clear() {
mCleared = true; if (mBagOfTags != null) { synchronized (mBagOfTags) { for (Object value : mBagOfTags.values()) {
closeWithRuntimeException(value);
}
}
}
onCleared();
}
<T> T setTagIfAbsent(String key, T newValue) {
T previous; synchronized (mBagOfTags) {
previous = (T) mBagOfTags.get(key); if (previous == null) {
mBagOfTags.put(key, newValue);
}
}
T result = previous == null ? newValue : previous; if (mCleared) {
closeWithRuntimeException(result);
} return result;
}private static void closeWithRuntimeException(Object obj) { if (obj instanceof Closeable) { try {
((Closeable) obj).close();
} catch (IOException e) { throw new RuntimeException(e);
}
}
}
复制代码
同样的通过上面的总结,我们也为
ViewModel扩展一套常用的方法
inline fun ViewModel.requestMain(
errCode: Int = -1, errMsg: String = "", report: Boolean = false,
noinline block: suspend CoroutineScope.() -> Unit) {
viewModelScope.launch(GlobalCoroutineExceptionHandler(errCode, errMsg, report)) {
block.invoke(this)
}
}
inline fun ViewModel.requestIO(
errCode: Int = -1, errMsg: String = "", report: Boolean = false,
noinline block: suspend CoroutineScope.() -> Unit) {
viewModelScope.launch(Dispatchers.IO + GlobalCoroutineExceptionHandler(errCode, errMsg, report)) {
block.invoke(this)
}
}
inline fun ViewModel.delayMain(
errCode: Int = -1, errMsg: String = "", report: Boolean = false, delayTime: Long,
noinline block: suspend CoroutineScope.() -> Unit) {
viewModelScope.launch(GlobalCoroutineExceptionHandler(errCode, errMsg, report)) {
withContext(Dispatchers.IO) {
delay(delayTime)
}
block.invoke(this)
}
}
复制代码
然后我们就可以愉快的在
ViewModel进行使用协程了。
class MainViewModel:ViewModel() {
init {
requestMain {
Log.d("MainViewModel", "主线程中启动协程")
}
requestIO {
Log.d("MainViewModel", "IO线程中启动协程进行网络加载")
}
delayMain(100){
Log.d("MainViewModel", "主线程中启动协程并延时一定时间")
}
}
}
复制代码
好了,常规使用协程的方式我都已经学会。但是我们在一些环境下如法使用使用
lifecycleScope和
viewModelScope的时候我们又该怎么办。比如:在
Service、
Dialog、
PopWindow以及一些其他的环境中又该如何使用。
在这些环境中我们可以采用通用的方式进行处理,其实还是根据协程作用域的差异分为两类:
协同作用域:这一类我们就模仿
MainScope自定义一个
CoroutineScope。主从(监督)作用域:这一类我们直接使用
MainScope,然后在此基础上做一些扩展即可。如果对这两个概念还不理解的,麻烦移步到第二章节里面仔细阅读一遍,这里就不再解释。
我们接下来模仿
MainScope创建一个
CoroutineScope,它是在主线程下执行,并且它的
Job不是
SupervisorJob。
@Suppress("FunctionName")public fun NormalScope(): CoroutineScope = CoroutineScope(Dispatchers.Main)
复制代码
然后我再基于
NormalScope和
MainScope进行使用。我们就以
Service为例来实现。
abstract class BaseService :Service(){ private val normalScope = NormalScope()
override fun onDestroy() {
normalScope.cancel()
super.onDestroy()
} protected fun requestMain(
errCode: Int = -1, errMsg: String = "", report: Boolean = false,
block: suspend CoroutineScope.() -> Unit) {
normalScope.launch(GlobalCoroutineExceptionHandler(errCode, errMsg, report)) {
block.invoke(this)
}
} protected fun requestIO(
errCode: Int = -1, errMsg: String = "", report: Boolean = false,
block: suspend CoroutineScope.() -> Unit): Job { return normalScope.launch(Dispatchers.IO + GlobalCoroutineExceptionHandler(errCode, errMsg, report)) {
block.invoke(this)
}
} protected fun delayMain(
delayTime: Long,errCode: Int = -1, errMsg: String = "", report: Boolean = false,
block: suspend CoroutineScope.() -> Unit) {
normalScope.launch(GlobalCoroutineExceptionHandler(errCode, errMsg, report)) {
withContext(Dispatchers.IO) {
delay(delayTime)
}
block.invoke(this)
}
}
}
复制代码
我们创建一个抽象类
BaseService类,然后再定义一些基础使用方法后,我就可以快速的使用了
class MainService : BaseService() {
override fun onBind(intent: Intent): IBinder? = null
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
requestIO { //网络加载
} return super.onStartCommand(intent, flags, startId)
}
}
复制代码
同理在
Dialog、
PopWindow以及一些其他的环境中可以依照此方法,定义符合我们自己需求的
CoroutineScope。
一定要记得不要跨域使用,以及及时的关闭协程。
又到了文章末尾,在此章节中我们已经了解了协程结合
Activity、
Fragment、
Lifecycle、
Viewmodel的基础使用,以及如何简单的自定义一个协程,如果还有不清楚的地方,可在下方留言。
更多Android技术分享可以关注@我,也可以加入QQ群号:Android进阶学习群:345659112,一起学习交流。
作者:一个被摄影耽误的程序猿
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/69983917/viewspace-2795553/ ,如需转载,请注明出处,否则将追究法律责任。