安卓架构组件-依赖注入

· 浏览次数 : 0

小编点评

本文详细介绍了安卓开发中的依赖注入(DI)技术及其实现方式。首先定义了依赖注入的概念,并说明了依赖注入的好处,然后详细描述了三种主要的DI实现方法: 1. 手动实现依赖注入:通过在类中显式地接收依赖对象并管理其生命周期,这种方式容易导致代码重复和维护困难。 2. 构建依赖注入框架:如Dagger,通过注解自动生成所需的依赖对象,提高了开发效率,同时需要注意性能问题。 3. 应用程序限定作用域和Hilt:使用限定符和组件生命周期管理依赖注入对象,使依赖注入与组件生命周期绑定,提升了代码的可维护性和可重用性。 文章强调了依赖注入技术的优点,包括降低类之间的耦合程度、增加代码的可测试性和扩展性。同时,总结了Dagger和Hilt两种流行的DI框架,并指出了它们的应用场景和优势所在。 总体而言,依赖注入技术是提升安卓应用程序设计和开发效率的重要工具之一,它通过不同的实现方式在不同的项目中有各自的优势。根据项目的具体需求和特点,开发者可以选择最为适当的方法来实现依赖注入。

正文

安卓依赖注入

什么是依赖注入

依赖注入(DI,Dependency Injection)是一种广泛的编程技术。把依赖(所需对象)传递给其它对象创建,好处是类的耦合更加松散,遵循依赖倒置的原则。

类获取所需对象

class Engine {  
    fun start() {  
        println("engine start")  
    }  
}
class Car {  
    private val engine: Engine = Engine()  
  
    fun start(){  
        engine.start()  
    }  
}

Car对Engine强依赖,如果需要其它类型的Engine,需要增加一个新的Engine,必须对Car进行改动。

依赖注入获取所需对象

interface Engine {  
    fun start()  
}  
  
class VEngine : Engine{  
    override fun start() {  
        println("VEngine start")  
    }  
}  
  
class WEngine : Engine{  
    override fun start() {  
        println("WEngine start")  
    }  
}

class Car(private val engine: Engine) {  
    fun start(){  
        engine.start()  
    }  
}

在构造函数中接收Engine对象作为参数,而不是初始化时构造自己的Engine对象,这就叫做依赖注入。
依赖注入还有很多其它的方式,如变量的get/set,就一一不介绍了。

安卓中手动实现依赖注入

手动实现依赖注入,就是依赖注入的原理。依赖注入框架会生成同样功能的样板代码。
假设有个登录场景,流程大概是这样:
LoginActivity->LoginViewModel->UserRepository

class UserRepository(
	private val localDataSource: UserLocalDataSource,
    private val remoteDataSource: UserRemoteDataSource  
)
class UserLocalDataSource()
class UserRemoteDataSource(
	private val loginService: LoginRetrofitService  
)

在需要的位置手动注入

在需要的地方创建依赖,缺点比较明显:

  1. 大量的样板代码
  2. 必须需要按照顺序声明依赖
  3. 复用困难。
/**
* 在LoginActivity的onCreate函数里:
*/
//创建UserRemoteDataSource需要的依赖
val retrofit = Retrofit.Builder()
.baseUrl("https://example.com")
.build()
.create(LoginService::class.java)
//创建Repository需要的依赖
val remoteDataSource = UserRemoteDataSource(retrofit)
val localDataSource = UserLocalDataSource()
//创建ViewModel需要的依赖
val userRepository = UserRepository(localDataSource, remoteDataSource)
//创建ViewModel
loginViewModel = LoginViewModel(userRepository)

使用容器管理依赖

如果想要复用对象,可以创建一个类来初始化所需的依赖。

class AppContainer {
	private val retrofit = Retrofit.Builder()
	.baseUrl("https://example.com").build()
	.create(LoginService::class.java)
	private val remoteDataSource = UserRemoteDataSource(retrofit)
	private val localDataSource = UserLocalDataSource()
	val userRepository = UserRepository(localDataSource, remoteDataSource)  
}

如果需要在整个app中使用,可以放到application中:

class MyApplication : Application(){
	val appContainer = AppContainer()
}
/**
* 在LoginActivity的onCreate函数里:
*/
val appContainer = (application as MyApplication).appContainer  
loginViewModel = LoginViewModel(appContainer.userRepository)

使用容器来管理依赖还是有样板代码,且需要手动为依赖项创建实例对象。

管理依赖项的声明周期

之前把UserRepository的生命周期放在了application,在app被关闭前永远不会被释放。如果数据非常大,会导致内存占用过高。
假如,只有在LoginActivity需要依赖,其它位置不需要依赖:

  1. AppContainer 内部需要新增一个LoginContainer,用来存放UserRepository。
  2. 在LoginActivity:onCreate时手动创建LoginContainer并放到AppContainer,onDestory时把AppContainer设置为null,主动释放引用。
    根据生命周期来管理依赖项,这样时比较合理的。

Dagger实现依赖注入

什么是Dagger

Dagger是一个依赖注入的库,通过自动生成代码的方式,实现依赖注入。由于是在编译时生成的代码,性能会高于基于反射的方案。Dagger生成的代码和手动实现依赖注入生成的代码相似。

  • 每次调用函数都重新创建对象
//@Inject 注解会告诉Dagger,需要注入.
class UserRepository @Inject constructor(  
    private val localDataSource: UserLocalDataSource,  
    private val remoteDataSource: UserRemoteDataSource  
){  
    init {  
        println("UserRepository Created")  
    }  
}
class UserLocalDataSource @Inject constructor() {  
    init {  
        println("UserLocalDataSource Created")  
    }  
}
class UserRemoteDataSource @Inject constructor(){  
    init {  
        println("UserRemoteDataSource Created")  
    }  
}
//DaggerApplicationGraph.create() 会创建新对象  
private val applicationGraph:ApplicationGraph = DaggerApplicationGraph.create()  
//applicationGraph.repository() 会创建新对象  
private val repository1 = applicationGraph.repository()  
private val repository2 = applicationGraph.repository()
/**
输出如下:
UserLocalDataSource Created
UserRemoteDataSource Created
UserRepository Created
UserLocalDataSource Created
UserRemoteDataSource Created
UserRepository Created
*/
  • 首次创建对象,之后全局复用这个单例对象.
@Singleton  
class UserRepository @Inject constructor(  
    private val localDataSource: UserLocalDataSource,  
    private val remoteDataSource: UserRemoteDataSource  
){  
    init {  
        println("UserRepository Created")  
    }  
}
class UserLocalDataSource @Inject constructor() {  
    init {  
        println("UserLocalDataSource Created")  
    }  
}
class UserRemoteDataSource @Inject constructor(){  
    init {  
        println("UserRemoteDataSource Created")  
    }  
}
/**  
 * @Component 注解,用于 interface
 * Dagger会生成一个对应的类,以Dagger开头,ApplicationGraph就是DaggerApplicationGraph
 * 调用函数会返回对应的对象, Dagger会自动添加依赖
 * @Singleton 注解,用于标识为全局单例
 */
@Singleton  
@Component  
interface ApplicationGraph {  
    fun repository(): UserRepository  
}
@Singleton  
@Component  
interface LoginGraph {  
    fun repository(): UserRepository  
}
class One{  
    //DaggerApplicationGraph.create() 会创建新对象  
    private val applicationGraph:ApplicationGraph = DaggerApplicationGraph.create()  
    //applicationGraph.repository() 会创建新对象  
    private val repository1 = applicationGraph.repository()  
    private val repository2 = applicationGraph.repository()  
    init {  
        println("One Created")  
    }  
}
class Two{  
    private val loginGraph:LoginGraph = DaggerLoginGraph.create()  
    private val repository1 = loginGraph.repository()  
    private val repository2 = loginGraph.repository()  
    init {  
        println("Two Created")  
    }  
}
/**
One 和 Two 使用同一个对象,因为@Singleton注解是全局单例
输出如下:
UserLocalDataSource Created
UserRemoteDataSource Created
UserRepository Created
One Created
UserLocalDataSource Created
UserRemoteDataSource Created
UserRepository Created
Two Created
*/

在安卓中使用Dagger

  • 对Activity中的字段进行注入
/**  
 * 为了演示方便,这里没有继承ViewModel  
 */
class LoginViewModel @Inject constructor(  
    private val userRepository: UserRepository  
) {  
    init {  
        println("LoginViewModel Created")  
    }  
}
/**  
 * Activity 是用安卓系统实例化的,所以无法被Dagger创建  
 * 初始化的代码需要放在onCreate方法中  
 * 使用手动调用inject的方式,对字段进行注入,需要被注入的字段必须有@Inject注解  
 */  
class LoginActivity : AppCompatActivity() {  
    @Inject lateinit var loginViewModel: LoginViewModel  
  
    override fun onCreate(savedInstanceState: Bundle?) {  
        //调用inject,告诉Dagger,可以对当前对象的@Inject字段进行注入了  
        (applicationContext as MyApplication).applicationGraph.inject(this)  
        //调用完成,loginViewModel可以使用了  
  
        super.onCreate(savedInstanceState)  
        setContentView(R.layout.activity_login)  
    }  
}
/**  
 * @Component 注解,用于 interface * Dagger会生成一个对应的类  
 * 调用函数会返回对应的对象, Dagger会自动添加依赖  
 * @Singleton 注解,用于标识为全局单例  
 * inject 调用函数手动注入带有@Inject注解的字段,函数名称是任意的,参数是需要注入的对象.
 */
@Singleton  
@Component  
interface ApplicationGraph {  
    fun repository(): UserRepository  
    fun inject(activity: LoginActivity)  
}
class MyApplication : Application() {  
    val applicationGraph: ApplicationGraph = DaggerApplicationGraph.create()  
}
  • 主动告知如何提供实例
//增加一个参数
class UserRemoteDataSource @Inject constructor(private val loginService: LoginService){  
    init {  
        println("UserRemoteDataSource Created")  
    }  
    fun login(){  
        loginService.login()  
    }  
}
//LoginService 只能通过Builder.create()创建
interface LoginService {  
    private class LoginServiceImpl : LoginService{  
        init {  
            println("LoginServiceImpl Created")  
        }  
    }  
    fun login() = println("login")  
    class Builder{  
        fun create(): LoginService {  
            return LoginServiceImpl()  
        }  
    }  
}
/**  
 * @DisableInstallInCheck 用于屏蔽绑定生命周期,这个是hilt的内容.  
 * @Module 是dagger的注解,用来告诉dagger可以提供实例对象.  
 * @Provides 用于@Module注解内,提供对应类型的实例对象,函数名任意.也可以添加@Singleton注解创建单例.
 */
@DisableInstallInCheck  
@Module  
class LoginModule {  
    @Provides  
    fun providerLoginService(): LoginService{  
        return LoginService.Builder().create()  
    }  
}
/**  
 * @Component 注解,用于 interface * Dagger会生成一个对应的类,以Dagger开头,ApplicationGraph就是DaggerApplicationGraph  
 * modules 参数用来指定对象该如何提供,必须带有@Module注解  
 * 调用函数会返回对应的对象, Dagger会自动添加依赖  
 * @Singleton 注解,用于标识为全局单例  
 * inject 调用函数手动注入带有@Inject注解的字段,函数名称是任意的,参数是需要注入的对象.  
 */
@Singleton  
@Component(modules = [LoginModule::class])  
interface ApplicationGraph {  
    fun repository(): UserRepository  
  
    fun inject(activity: LoginActivity)  
}
  • 子组件和作用域,限定作用域为生命周期
class MyApplication : Application() {  
    val applicationGraph: ApplicationComponent = DaggerApplicationComponent.create()  
}
@Singleton  
@Component(modules = [LoginModule::class, Subcomponent::class])  
interface ApplicationComponent {  
    fun loginComponent(): LoginComponent.Factory  
}
@Scope  
@Retention(value = AnnotationRetention.RUNTIME)  
annotation class ActivityScope
@ActivityScope  
class LoginViewModel @Inject constructor(  
    private val userRepository: UserRepository,  
) {  
    init {  
        println("LoginViewModel Created")  
    }  
    fun login(){  
        userRepository.login()  
    }  
}
@ActivityScope  
@Subcomponent  
interface LoginComponent {  
  
    @Subcomponent.Factory  
    interface Factory{  
        fun create(): LoginComponent  
    }  
  
    fun inject(activity: LoginActivity)  
    fun inject(fragment: LoginFragment)  
  
    fun repository(): UserRepository  
}
@DisableInstallInCheck  
@Module  
class LoginModule {  
    @Singleton  
    @Provides    fun providerLoginService(): LoginService{  
        return LoginService.Builder().create()  
    }  
}
@ActivityScope  
class UserRepository @Inject constructor(  
    private val localDataSource: UserLocalDataSource,  
    private val remoteDataSource: UserRemoteDataSource  
){  
    init {  
        println("UserRepository Created")  
    }  
  
    fun login(){  
        remoteDataSource.login()  
    }  
}
class UserRemoteDataSource @Inject constructor(  
    private val loginService: LoginService,  
    private val loginService2: LoginService,  
){  
    init {  
        println("UserRemoteDataSource Created")  
    }  
    fun login(){  
        loginService.login()  
    }  
}
class UserLocalDataSource @Inject constructor() {  
    init {  
        println("UserLocalDataSource Created")  
    }  
}
class LoginActivity : AppCompatActivity() {  
    lateinit var loginComponent: LoginComponent  
  
    @Inject lateinit var loginViewModel: LoginViewModel  
  
    override fun onCreate(savedInstanceState: Bundle?) {  
        loginComponent = (application as MyApplication).applicationGraph.loginComponent().create()  
        loginComponent.inject(this)  
  
        super.onCreate(savedInstanceState)  
        setContentView(R.layout.activity_login)  
  
        findViewById<Button>(R.id.activity_login_bt_open).setOnClickListener {  
            startActivity(Intent(this, LoginActivity::class.java))  
        }  
    }  
}
class LoginFragment : Fragment() {  
    @Inject  
    lateinit var loginViewModel: LoginViewModel  
    override fun onCreateView(  
        inflater: LayoutInflater, container: ViewGroup?,  
        savedInstanceState: Bundle?  
    ): View? {  
        (activity as LoginActivity).loginComponent.inject(this)  
        val view = inflater.inflate(R.layout.fragment_login, container, false)  
        view.findViewById<Button>(R.id.fragment_login_bt_login).setOnClickListener {  
            loginViewModel.login()  
        }  
        return view  
    }  
}
@ActivityScope  
class LoginViewModel @Inject constructor(  
    private val userRepository: UserRepository,  
) {  
    init {  
        println("LoginViewModel Created")  
    }  
    fun login(){  
        userRepository.login()  
    }  
}
interface LoginService {  
    private class LoginServiceImpl : LoginService{  
        init {  
            println("LoginServiceImpl Created")  
        }  
    }  
    fun login() = println("login")  
    class Builder{  
        fun create(): LoginService {  
            return LoginServiceImpl()  
        }  
    }  
}
@ActivityScope  
@Subcomponent  
interface LoginComponent {  
  
    @Subcomponent.Factory  
    interface Factory{  
        fun create(): LoginComponent  
    }  
  
    fun inject(activity: LoginActivity)  
    fun inject(fragment: LoginFragment)  
  
    fun repository(): UserRepository  
}

通过限定作用域的方式,把依赖注入和生命周期绑定,Activity和Activity中的Fragment使用同一个ViewModel。
Dagger虽然可以实现精细的依赖注入,但是使用起来非常繁琐。

Hilt实现依赖注入

什么是Hilt

Hilt是基于Dagger构建的用于安卓的依赖注入库,简化在安卓上实现依赖注入。

把依赖项注入安卓类

Hilt可以很方便的注入到安卓类中
比如把ViewModel

class UserRepository @Inject constructor(  
    private val localDataSource: UserLocalDataSource,  
    private val remoteDataSource: UserRemoteDataSource  
){  
    init {  
        println("UserRepository Created")  
    }  
    fun login(){  
        remoteDataSource.login()  
    }  
}
class UserLocalDataSource @Inject constructor() {  
    init {  
        println("UserLocalDataSource Created")  
    }  
}
class UserRemoteDataSource @Inject constructor(){  
    init {  
        println("UserRemoteDataSource Created")  
    }  
    fun login(){  
        println("login")  
    }  
}
class LoginViewModel @Inject constructor(  
    private val userRepository: UserRepository,  
) {  
    init {  
        println("LoginViewModel Created")  
    }  
    fun login(){  
        userRepository.login()  
    }  
}
/**  
 * @AndroidEntryPoint 注解,可以被Hilt注入  
 */  
@AndroidEntryPoint  
class LoginActivity : AppCompatActivity() {  
    @Inject lateinit var viewModel: LoginViewModel  
  
    override fun onCreate(savedInstanceState: Bundle?) {  
        super.onCreate(savedInstanceState)  
        setContentView(R.layout.activity_login)  
  
        findViewById<Button>(R.id.activity_login_bt_open).setOnClickListener {  
            startActivity(Intent(this, LoginActivity::class.java))  
        }  
    }  
}
class LoginFragment : Fragment() {  
    lateinit var viewModel: LoginViewModel  
  
    override fun onCreateView(  
        inflater: LayoutInflater, container: ViewGroup?,  
        savedInstanceState: Bundle?  
    ): View? {  
        val view = inflater.inflate(R.layout.fragment_login, container, false)  
        viewModel = (activity as LoginActivity).viewModel  
        view.findViewById<Button>(R.id.fragment_login_bt_login).setOnClickListener {  
            viewModel.login()  
        }  
        return view  
    }  
}
/**  
 * @HiltAndroidApp 注解:  
 * 必须在Application 中添加, Hilt会生成一个类作为依赖的容器, 作为依赖注入的入口.  
 */@HiltAndroidApp  
class MyApplication : Application()

Hilt模块

模块的Bind

可以被直接构造的接口实现可以通过Bind注入

class UserRemoteDataSource @Inject constructor(  
    private val analyticsService: AnalyticsService  
){  
    init {  
        println("UserRemoteDataSource Created")  
    }  
    fun login(){  
        println("login")  
        analyticsService.logEvent("login")  
    }  
}
interface AnalyticsService {  
    fun logEvent(eventName: String)  
}
class AnalyticsServiceImpl @Inject constructor(  
    @ApplicationContext val context: Context  
) : AnalyticsService {  
    override fun logEvent(eventName: String) {  
        println("Context: $context LogEvent: $eventName")  
    }  
}
@Module  
@InstallIn(ActivityComponent::class)  
abstract class AnalyticsServiceModule {  
    @Binds  
    abstract fun bindAnalyticsService(analyticsService: AnalyticsServiceImpl): AnalyticsService  
}

模块的Provider

无法被直接构造的接口实现可以通过Provider注入

@Module  
@InstallIn(ActivityComponent::class)  
class LoginServiceModule {  
    @Provides  
    fun provideLoginService(): LoginService {  
        return LoginServiceImpl.Builder().build()  
    }  
}
interface LoginService {  
    fun login()  
}
class LoginServiceImpl private constructor(): LoginService {  
    class Builder {  
        fun build(): LoginService {  
            return LoginServiceImpl()  
        }  
    }  
    init {  
        println("LoginServiceImpl Created")  
    }  
    override fun login() {  
        println("login")  
    }  
}
class UserRemoteDataSource @Inject constructor(  
    private val analyticsService: AnalyticsService,  
    private val loginService: LoginService  
){  
    init {  
        println("UserRemoteDataSource Created")  
    }  
    fun login(){  
        loginService.login()  
        analyticsService.logEvent("login")  
    }  
}

同一类型提供多个绑定

Provides如果不带限定标签,只会返回一种类型的实现。
可以通过限定标签来区分,返回对应的实现。
Hilt限定符默认提供了 @ApplicationContex 和 @ActivityContext,用来提供两种不同类型的Context

@Qualifier  
@Retention(AnnotationRetention.BINARY)  
annotation class DebugLog  
  
@Qualifier  
@Retention(AnnotationRetention.BINARY)  
annotation class ErrorLog  
  
@Module  
@InstallIn(SingletonComponent::class)  
object LogServiceModule {  
    @DebugLog  
    @Provides    
    fun provideDebugLogger(): LogService {  
        return LogServiceDebugImpl()  
    }  
  
    @ErrorLog  
    @Provides    
    fun provideErrorLogger(): LogService {  
        return LogServiceErrorImpl()  
    }  
}
@Module  
@InstallIn(ActivityComponent::class)  
class LoginServiceModule {  
    @Provides  
    fun provideLoginService(@DebugLog logService: LogService): LoginService {  
        return LoginServiceImpl.Builder().build(logService)  
    }  
}
class LoginServiceImpl private constructor(val logService:LogService): LoginService {  
    class Builder {  
        fun build(logService:LogService): LoginService {  
            return LoginServiceImpl(logService)  
        }  
    }  
    init {  
        logService.log("LoginServiceImpl Created")  
    }  
    override fun login() {  
        logService.log("login")  
    }  
}

class AnalyticsServiceImpl @Inject constructor(  
    @ApplicationContext val context: Context,  
    @ErrorLog val logService: LogService  
) : AnalyticsService {  
    override fun logEvent(eventName: String) {  
        logService.log("Context: $context LogEvent: $eventName")  
    }  
}

Hilt为安卓类生成的组件

组件生命周期和作用域

Hilt组件 创建时机 销毁时机 作用域 备注
SingletonComponent Application#onCreate() Application被销毁 @Singleton 相当于是单例的
ActivityComponent Activity#onCreate() Activity#onDestroy() @ActivityScoped 会随着生命周期注入
ActivityRetainedComponent 首次Activity#onCreate() 最后一次Activity#onDestroy() @ActivityRetainedScoped Fragment的ViewModel会随着Fragment回收,但ActivityRetainedComponent只会随着Activity回收,比ViewModel生命周期更长。
ViewModelComponent ViewModel 已创建 ViewModel 已销毁 @ViewModelScoped 和ViewModel的生命周期相同
ViewComponent View#super() View 已销毁 @ViewScoped 和View的生命周期相同
ViewWithFragmentComponent View#super() View的拥有者被销毁 @ViewScoped带有 @WithFragmentBindings注解的View 比如Fragment导航离开屏幕,Fragment还在,但View被销毁时仍然保留。
FragmentComponent Fragment#onAttach() Fragment#onDestroy() @FragmentScoped 和Fragment的生命周期相同
ServiceComponent Service#onCreate() Service#onDestroy() @ServiceScoped 和Service的生命周期相同

组件默认绑定

可以使用Model安装到默认绑定,实现注入。比如上面提到的"同一类型提供多个绑定"

安卓组件 默认绑定
SingletonComponent Application
ActivityRetainedComponent Application
ViewModelComponent SavedStateHandle
ActivityComponent Application、Activity
FragmentComponent Application、Activity、Fragment
ViewComponent Application、Activity、View
ViewWithFragmentComponent Application、Activity、Fragment、View
ServiceComponent Application、Service

在Hilt不支持的类中注入依赖项

使用@EntryPoint让任意接口可以被注入.
使用@EntryPointAccessors获取被注入的对象.
因为是SingletonComponent,所以要使用Application的Context 。如果是ActivityComponent就需要使用Activity的Context。

class ExampleContentProvider : ContentProvider() { 
	@EntryPoint
	@InstallIn(SingletonComponent::class)
	interface ExampleContentProviderEntryPoint { 
		fun analyticsService(): AnalyticsService 
	}
	fun doSomeThing(){
		val appContext = context?.applicationContext ?: throw IllegalStateException()
		val hiltEntryPoint = EntryPointAccessors.fromApplication(appContext, ExampleContentProviderEntryPoint::class.java)
		val analyticsService = hiltEntryPoint.analyticsService()
	}
}

与安卓架构组件-依赖注入相似的内容:

安卓架构组件-依赖注入

安卓依赖注入 什么是依赖注入 依赖注入(DI,Dependency Injection)是一种广泛的编程技术。把依赖(所需对象)传递给其它对象创建,好处是类的耦合更加松散,遵循依赖倒置的原则。 类获取所需对象 class Engine { fun start() { println("engine

详解安卓架构入门

准备 首先进入安卓架构入门的代码仓库: Android Architecture Starter Templates: https://github.com/android/architecture-templates 先看看介绍,简单分析一下: 架构入门的模板 UI 界面非常简陋 Navigati

安卓开发日志采集和分析面面谈

明确系统提供了哪些日志,cpu、meminfo、app、log等,我们怎么看日志,看日志的三步骤,怎么用工具提高分析日志的效率,bugreport和chkbugreport。

FFmpeg开发笔记(三十一)使用RTMP Streamer开启APP直播推流

​RTMP Streamer是一个安卓手机端的开源RTMP直播推流框架,可用于RTMP直播和RTSP直播,其升级版还支持SRT直播(腾讯视频云就采用SRT协议)。RTMP Streamer支持的视频编码包括H264、H265、AV1等等,支持的音频编码包括AAC、G711、OPUS等等,可谓功能强大

移动端termux安装kali

1.相关准备一部安卓手机,termux,NVAC,浏览器2.安装kali首先进入kali的官网选择文档找到Android手机上的kali找到NetHunter-Rootless找到kali安装命令:termux-setup-storagepkg install wgetwget -O install

手机用户的开源福音「GitHub 热点速览」

不知道多少用安卓机的小伙伴,被开屏广告烦过。相比有些克制的 iOS 机,安卓机是个应用基本上都有开屏广告,少则 3s 多则 10s,本周获得 1k+ star 的 Android-Touch-Helper 就是帮你免去看广告烦恼的项目。此外,iOS 和 Android 双系统之间的媒体资料传递也有新法子,NearDrop 让你用苹果设备给安卓设备投递照片。

uiautomator2环境搭建+元素定位(安卓)

一.环境搭建 1.安装uiautomator2 在终端使用pip安装即可 pip install uiautomator2 2.安装adb 可参考:https://www.cnblogs.com/lihongtaoya/p/14970306.html 3.安装weditor 终端执行如下命令 pip

《吐血整理》高级系列教程-吃透Fiddler抓包教程(32)-Fiddler如何抓取IOS系统中Flutter应用程序的包

1.简介 上一篇讲解了安卓手机可以通过VPN代理来抓取Flutter应用程序的包,iOS(iphone)同样使用上一篇中VPN方法(原理与android是一致的),同样需要使用到VPN,在iOS也有许多与drony功能类似的软件,大家可以自己选择自己喜欢的使用,宏哥这里使用的是Shadowrocke

r0capture 原理分析

r0capture 是比较好用的抓包工具 仅限安卓平台,测试安卓7、8、9、10、11、12 可用 ; 无视所有证书校验或绑定,不用考虑任何证书的事情; 通杀TCP/IP四层模型中的应用层中的全部协议; 通杀协议包括:Http,WebSocket,Ftp,Xmpp,Imap,Smtp,Protobu

[转帖]A17再次证明苹果才是王者,组装芯片的安卓手机给它提鞋都不配

http://news.sohu.com/a/653472711_121124371 在挤了两代牙膏之后,苹果终于拿出了性能大幅提升的A17处理器,外媒传出A17处理器的性能提升幅度至少超过四成,相比起此前两代处理器仅提高一成左右,显示出苹果终于拿出了杀手锏。 外媒给出的数据指A17工程机的单核性能