compose--附带效应、传统项目集成、导航

该文章将是compose基础系列中最后一篇,附带效应是这篇文章的重点,其余补充内容为如何在传统xml中集成composecompose导航的使用

一、附带效应

有了前面的了解,我们知道compose中是由State状态发生改变来使得可组函数发生重组,状态的改变应该是在可组合函数作用域中,但有时我们需要它发生在别的作用域,如定时弹出一个消息,这就需要附带效应出场了,compose定义了一系列附带效应API,来运用在可组合函数作用域内外,发生状态改变的不同场景

1.LaunchedEffect

LaunchedEffect我们之前就已经使用过了,特别是在低级别动画时,LaunchedEffect用于安全地调用挂起函数,本质就是启动一个协程,LaunchedEffect的调用需要在可组合函数作用域内

LaunchedEffect的执行分为以下三种,优先级由上到下:

  • 当发生重组时LaunchedEffect退出组合,将取消协程
  • 当发生重组时如果LaunchedEffect使用的同一个key,并且上次LaunchedEffect没执行结束,则不执行
  • 当发生重组时如果LaunchedEffect使用的不同的key,并且上次LaunchedEffect没执行结束,则取消上次执行,启动新的协程执行该次任务

例子:

@Preview
@Composable
fun MyLaunchEffect() {
    var state by remember { mutableStateOf(false) }
    var count by remember { mutableStateOf(0) }

    if (state) {
        // key为Unit唯一值
        LaunchedEffect(Unit) {
            delay(3000)
            count++
        }
    }

    Box(modifier = Modifier
        .size(50.dp)
        .background(Color.Cyan)
        .clickable { state = !state }
    ) {
        Text("执行了${count}次")
    }
}

先是点击两下的效果,由于statefalse时,没有LaunchedEffect的代码块,此时LaunchedEffect会取消:

稍微改变下例子的代码,一旦状态发生改变,那么重复执行LaunchedEffect

@Preview
@Composable
fun MyLaunchEffect2() {
    var state by remember { mutableStateOf(0) }
    var count by remember { mutableStateOf(0) }

    if (state > 0) {
        // key为Unit唯一值
        LaunchedEffect(Unit) {
            delay(3000)
            count++
        }
    }

    Box(modifier = Modifier
        .size(50.dp)
        .background(Color.Cyan)
        .clickable { state++ }
    ) {
        Text("执行了${count}次")
    }
}

点击三下的效果,LaunchedEffectkey唯一,并且重复执行,由于第一次没执行完,所以只会执行第一次的LaunchedEffect

改变例子代码,每次执行的key不同:

@Preview
@Composable
fun MyLaunchEffect3() {
    var state by remember { mutableStateOf(0) }
    var count by remember { mutableStateOf(0) }

    if (state > 0) {
        // key为随机值
        LaunchedEffect(UUID.randomUUID()) {
            delay(3000)
            // 置为0,防止不断重组导致一直执行LaunchedEffect
            state = 0
            count++
        }
    }

    Box(modifier = Modifier
        .size(50.dp)
        .background(Color.Cyan)
        .clickable { state++ }
    ) {
        Text("执行了${count}次")
    }
}

效果,取消了之前的LaunchedEffect,隔了3秒后才发生count状态改变:

2.rememberCoroutineScope

rememberCoroutineScope也是使用过的,它返回一个remember的协程作用域,可以在可组合函数外使用,调用几次执行几次

例子:

@Preview
@Composable
fun MyRememberCoroutineScope() {
    val scope = rememberCoroutineScope()
    var count by remember { mutableStateOf(0) }

    Box(modifier = Modifier
        .size(50.dp)
        .background(Color.Cyan)
        .clickable {
            scope.launch {
                delay(3000)
                count++;
            }
        }
    ) {
        Text("执行了${count}次")
    }
}

效果:

3.rememberUpdatedState

LaunchedEffect一旦启动,同一个key其内部的方法调用和引用都是final的,即无法更改,如果LaunchedEffect内使用的外部引用可能发生改变,应该使用rememberUpdatedState

3.1 不使用remember

先来看一个例子,我在重组时生成一个随机数,并作为onTimeout()的打印参数,将onTimeout()传给MyRememberUpdatedStateLaunchedEffect内调用onTimeout()打印这个随机数:

@Preview
@Composable
fun MyTimeout() {
    var state by remember { mutableStateOf(false) }

    Column {
        // 1.生成随机数
        val random = Random.nextInt()
        Log.i("onTimeout", "return : $random")
        MyRememberUpdatedState(state) {
            // 4.打印随机数
            Log.i("onTimeout", "onTimeout() return : $random")
        }

        Button(onClick = { state = !state }) {
            Text("click")
        }
    }
}

@Composable
fun MyRememberUpdatedState(enable: Boolean, onTimeout: () -> Unit) {
    // 使用rememberUpdatedState
//    val rememberUpdatedState by rememberUpdatedState(onTimeout)
    val rememberUpdatedState = onTimeout

    // 2.key唯一发生重组,不会重新执行
    LaunchedEffect(true) {
        delay(5000)
        // 3.延迟5s,调用外部传入的onTimeout()
        rememberUpdatedState()
    }

    if (enable)
        Text("hi")
    else
        Text("hello")
}

我点击多次,这次的效果直接看日志即可:

可以看到最后打印的结果,是第一次生成的随机数

3.2 使用remember

我们尝试使用remember,将onTimeout作为State状态并记住,并以onTimeout作为key使得每次onTimeout发生改变,触发值的更新:

@Preview
@Composable
fun MyTimeout() {
    var state by remember { mutableStateOf(false) }

    Column {
        // 1.生成随机数
        val random = Random.nextInt()
        Log.i("onTimeout", "return : $random")
        MyRememberUpdatedState(state) {
            // 4.打印随机数
            Log.i("onTimeout", "onTimeout() return : $random")
        }

        Button(onClick = { state = !state }) {
            Text("click")
        }
    }
}

@Composable
fun MyRememberUpdatedState(enable: Boolean, onTimeout: () -> Unit) {
    // 使用rememberUpdatedState
//    val rememberUpdatedState by rememberUpdatedState(onTimeout)
    val rememberUpdatedState by remember(onTimeout) { mutableStateOf(onTimeout) }
//    val rememberUpdatedState = onTimeout

    // 2.key唯一发生重组,不会重新执行
    LaunchedEffect(true) {
        delay(5000)
        // 3.延迟5s,调用外部传入的onTimeout()
        rememberUpdatedState()
    }

    if (enable)
        Text("hi")
    else
        Text("hello")
}

打印的结果,依然是第一次生成的随机数:

3.3 使用rememberUpdatedState

rememberUpdatedState可以始终保持最新的值,从而改变LaunchedEffect运行时的引用的值

@Preview
@Composable
fun MyTimeout() {
    var state by remember { mutableStateOf(false) }

    Column {
        // 1.生成随机数
        val random = Random.nextInt()
        Log.i("onTimeout", "return : $random")
        MyRememberUpdatedState(state) {
            // 4.打印随机数
            Log.i("onTimeout", "onTimeout() return : $random")
        }

        Button(onClick = { state = !state }) {
            Text("click")
        }
    }
}

@Composable
fun MyRememberUpdatedState(enable: Boolean, onTimeout: () -> Unit) {
    // 使用rememberUpdatedState
    val rememberUpdatedState by rememberUpdatedState(onTimeout)
//    val rememberUpdatedState by remember{ mutableStateOf(onTimeout) }
//    val rememberUpdatedState = onTimeout

    // 2.key唯一发生重组,不会重新执行
    LaunchedEffect(true) {
        delay(5000)
        // 3.延迟5s,调用外部传入的onTimeout()
        rememberUpdatedState()
    }

    if (enable)
        Text("hi")
    else
        Text("hello")
}

打印结果:

原理:首先我们知道remember相当于创建了一个静态变量,如果不指定key,只会初始化一次,重复调用remember并不会更新引用,指定key时,当key发生变化,则会更新引用 LaunchedEffect运行时会复制引用,新建变量指向传入的引用,所以此时无论外部变量的引用发生如何改变,并不会改变LaunchedEffect内部变量的引用 rememberUpdatedStateremember的基础上做了更新值处理,每次调用到rememberUpdatedState时,将值更新,也就是引用的值的更新,此时不管外部变量还是LaunchedEffect内部变量的值引用都会发生变化,LaunchedEffect调用的自然就是最新的方法了,下面是rememberUpdatedState的源码:

@Composable
fun <T> rememberUpdatedState(newValue: T): State<T> = remember {
    mutableStateOf(newValue)
}.apply { value = newValue }

4.DisposableEffect

DisposableEffect可以在key变化和移除时做一些善后工作,需实现onDispose

例子:

@Preview
@Composable
fun MyDisposableEffect() {
    var state by remember { mutableStateOf(false) }
    var text by remember { mutableStateOf("click") }
    val scope = rememberCoroutineScope()

    if (state) {
        // 重组或移除时会调用onDispose
        DisposableEffect(Unit) {
            val job = scope.launch { 
                delay(3000)
                text = "点了"
            }

            onDispose {
                job.cancel()
                text = "取消了"
            }
        }
    }

    Button(onClick = { state = !state }) {
        Text(text)
    }
}

效果,在3s内点击了两次,导致重组时移除DisposableEffect而触发onDispose

5.SideEffect

SideEffect会在可组合函数重组完成时调用,可以进行用户行为分析、日志记录等操作

例子:

@OptIn(ExperimentalAnimationApi::class)
@Preview
@Composable
fun MySideEffect() {
    var enable by remember { mutableStateOf(false) }

    Column {
        AnimatedVisibility(
            visible = enable,
            enter = scaleIn(tween(2000)),
            exit = scaleOut(tween(2000))
        ) {
            MySideEffectText("hello world")
        }

        Button(onClick = { enable = !enable }) {
            Text("click")
        }
    }
}

@Composable
fun MySideEffectText(text: String) {
    SideEffect {
        Log.i("SideEffect", "重组完成")
    }

    Text(text)
}

效果,如果组件重组完成了,连续点击导致动画重复执行,则不会触发重组:

6.produceState

produceState 会启动一个协程,并返回一个State对象,用来将非 Compose 状态转换为 Compose 状态,即执行一些耗时操作,如网络请求,并将结果作为State对象返回

例子:

@Preview
@Composable
fun MyProduceState() {
    var visiable by remember { mutableStateOf(false) }

    Column {
        if (visiable)
            Text(load().value)

        Button(onClick = { visiable = !visiable }) {
            Text("load")
        }
    }
}

@Composable
fun load(): State<String> {
    return produceState(initialValue = "", producer = {
        delay(2000);

        value = "hi"
    })
}

效果:

7.derivedStateOf

derivedStateOf可以将一个或多个状态对象转变为其他的状态对象,一旦状态发生改变,只会在用到该derivedStateOf状态的地方进行重组

例子,根据传入的list,过滤高亮的元素,并展示到列表中:

val alpha = arrayOf("a", "b", "c", "d", "e", "f", "g", "h")

@Preview
@Composable
fun MyDerivedStateOf() {
    val items = remember { mutableStateListOf<String>() }

    Column {
        Button(onClick = { items.add(alpha[Random.nextInt(alpha.size)]) }) {
            Text("Add")
        }

        DerivedStateOf(items, highPriorityKeywords = listOf("a", "b"))
    }
}

/**
 * 拥有highPriorityKeywords的优先显示
 */
@Composable
fun DerivedStateOf(
    lists: List<String>,
    highPriorityKeywords: List<String> = listOf("Review", "Unblock", "Compose")
) {
    // 需要高亮置顶的items
    val highPriorityLists by remember(highPriorityKeywords) {
        derivedStateOf { lists.filter { it in highPriorityKeywords } }
    }

    LazyColumn {
        items(highPriorityLists) { value ->
            Text(value, color = Color.Red)
        }
        items(lists) { value ->
            Text(value)
        }
    }
}

效果:

8.snapshotFlow

snapshotFlow可以将 ComposeState 转为Flow,当在 snapshotFlow 块中读取的 State 对象之一发生变化时,如果新值与之前发出的值不相等,Flow 会向其收集器发出新值

@Preview
@Composable
fun MySnapshotFlow() {
    val listState = rememberLazyListState()
    val list = remember {
        mutableListOf<Int>().apply {
            repeat(1000) { index ->
                this += index
            }
        }
    }

    LazyColumn(state = listState) {
        items(list) {
            Text("hi:${it}")
        }
    }

    LaunchedEffect(Unit) {
        snapshotFlow {
            listState.firstVisibleItemIndex
        }.collect { index ->
            Log.i("collect", "${index}")
        }
    }
}

滚动查看日志:

9.重启效应

Compose 中有一些效应(如 LaunchedEffectproduceStateDisposableEffect)会采用可变数量的参数和键来取消运行效应,并使用新的键启动一个新的效应。在实际开发中,灵活运用key是否唯一来使得是否需要重启效应

二、传统项目集成

官方推荐一次性替换整个布局,也可以替换部分布局,本身compose就兼容传统xml的方式,所以在传统的项目上集成compose很容易

1.xml中使用compose

xml中使用ComposeView,表示一个加载compose的控件:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".ComposeIntegrateActivity">

    <TextView
        android:id="@+id/textView2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="hello android"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <androidx.compose.ui.platform.ComposeView
        android:id="@+id/composeView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@id/textView2" />

</androidx.constraintlayout.widget.ConstraintLayout>

Activity中调用ComposeViewsetContent()方法,并使用compose:

class ComposeIntegrateActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_compose_integrate)

        val composeView = findViewById<ComposeView>(R.id.composeView)
        composeView.setContent {
            MyComposeApplicationTheme {
                MyText1()
            }
        }
    }

    @Composable
    fun MyText1() {
        Text("hi compose")
    }
}

启动效果:

2.fragment中使用

fragment中要多一步绑定View树生命周期:

class BlankFragment : Fragment() {

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        val disposeOnViewTreeLifecycleDestroyed =
            ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed
        val root = inflater.inflate(R.layout.fragment_blank, container, false)
        root.findViewById<ComposeView>(R.id.fragment_composeView).apply {
            setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
            setContent {
                MaterialTheme() {
                    // In Compose world
                    Text("Hello Compose!")
                }
            }
        }
        return root
    }
}

三、导航

compose定义了全新的导航API,下面来开始使用它

1.导入依赖

    def nav_version = "2.5.3"

    implementation "androidx.navigation:navigation-compose:$nav_version"

2.创建 NavHost

NavHost需要一个navController用于控制导航到那个可组合项,startDestination 初始的可组合项,以及NavGraphBuilder导航关系图

class NaviActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            MyComposeApplicationTheme {
                MyNavi()
            }
        }
    }
}

@Preview
@Composable
fun MyNavi() {
    val navController = rememberNavController()

    NavHost(navController = navController, startDestination = "home") {
        composable("home") { Home() }
        composable("message") { Message() }
        composable("mine") { Mine() }
    }
}

@Composable
fun Home() {
    Box(
        modifier = Modifier.fillMaxSize(),
        contentAlignment = Alignment.Center
    ) {
        Text("Home")
    }
}

@Composable
fun Message() {
    Box(
        modifier = Modifier.fillMaxSize(),
        contentAlignment = Alignment.Center
    ) {
        Text("Message")
    }
}

@Composable
fun Mine() {
    Box(
        modifier = Modifier.fillMaxSize(),
        contentAlignment = Alignment.Center
    ) {
        Text("Mine")
    }
}

效果:

3.navController

接下来使用navController来导航到不同的可组合项,下面是官方给出的示例的几种方式:

  • 在导航到“friendslist”并加到返回堆栈中
navController.navigate("friendslist")
  • 在导航到“friendslist”之前,将所有内容从后堆栈中弹出到“home”(不包含home)
navController.navigate("friendslist") {
    popUpTo("home")
}
  • 在导航到“friendslist”之前,从堆栈中弹出所有内容,包括“home”
navController.navigate("friendslist") {
    popUpTo("home") { inclusive = true }
}
  • 只有当我们还不在“search”时,才能导航到“search”目标地,避免在后堆栈的顶部有多个副本
navController.navigate("search") {
    launchSingleTop = true
}

例子:

我们给App添加上Scaffold,并在底部导航栏进行navController导航的控制

class NaviActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            MyComposeApplicationTheme {
                Scene()
            }
        }
    }
}

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun Scene() {
    val navController = rememberNavController()

    Surface(Modifier.background(MaterialTheme.colorScheme.surface)) {
        Scaffold(
            topBar = {
                TopAppBar(
                    title = {
                        Text(
                            stringResource(id = R.string.app_name),
                            color = MaterialTheme.colorScheme.onPrimaryContainer
                        )
                    },
                    colors = TopAppBarDefaults.smallTopAppBarColors(
                        containerColor = MaterialTheme.colorScheme.primaryContainer
                    )
                )
            },
            bottomBar = {
                Row(
                    modifier = Modifier
                        .fillMaxWidth()
                        .background(MaterialTheme.colorScheme.primaryContainer)
                        .padding(10.dp),
                    horizontalArrangement = Arrangement.SpaceAround
                ) {
                    CompositionLocalProvider(LocalContentColor provides MaterialTheme.colorScheme.onPrimaryContainer) {
                        Icon(
                            Icons.Rounded.Home, contentDescription = null,
                            modifier = Modifier.clickable {
                                navController.navigate("home") {
                                    launchSingleTop = true
                                    popUpTo("home")
                                }
                            }
                        )
                        Icon(
                            Icons.Rounded.Email, contentDescription = null,
                            modifier = Modifier.clickable {
                                navController.navigate("message") {
                                    launchSingleTop = true
                                    popUpTo("message")
                                }
                            }
                        )
                        Icon(
                            Icons.Rounded.Face, contentDescription = null,
                            modifier = Modifier.clickable {
                                navController.navigate("mine") {
                                    launchSingleTop = true
                                    popUpTo("mine")
                                }
                            }
                        )
                    }
                }
            }
        ) { paddings ->
            MyNavi(
                modifier = Modifier.padding(paddings),
                navController = navController,
                startDestination = "home"
            ) {
                composable("home") { Home() }
                composable("message") { Message() }
                composable("mine") { Mine() }
            }
        }
    }
}

@Composable
fun MyNavi(
    modifier: Modifier = Modifier,
    navController: NavHostController,
    startDestination: String,
    builder: NavGraphBuilder.() -> Unit
) {
    NavHost(
        modifier = modifier,
        navController = navController,
        startDestination = startDestination
    ) {
        builder()
    }
}

@Composable
fun Home() {
    Box(
        modifier = Modifier.fillMaxSize(),
        contentAlignment = Alignment.Center
    ) {
        Text("Home")
    }
}

@Composable
fun Message() {
    Box(
        modifier = Modifier.fillMaxSize(),
        contentAlignment = Alignment.Center
    ) {
        Text("Message")
    }
}

@Composable
fun Mine() {
    Box(
        modifier = Modifier.fillMaxSize(),
        contentAlignment = Alignment.Center
    ) {
        Text("Mine")
    }
}

效果:

4.参数传递

Navigation Compose 还支持在可组合项目的地之间传递参数,方式为Restful风格,这种风格的参数为必填:

MyNavi(
modifier = Modifier.padding(paddings),
navController = navController,
startDestination = "home/b1254"
) {
    composable("home/{userId}") { Home() }
    composable("message/{count}") { Message() }
    composable("mine/{userId}") { Mine() }
}

...

// 导航时带入参数
navController.navigate("mine/a1587")

参数类型默认为字符串,也可以通过navArgument指定参数的类型:

composable(
"home/{userId}",
arguments = listOf(navArgument("userId") { type = NavType.StringType })
) { Home() }

通过 lambda 中提供的NavBackStackEntry中提取这些参数:

composable(
"home/{userId}",
arguments = listOf(navArgument("userId") { type = NavType.StringType })
) {navBackStackEntry ->
    navBackStackEntry.arguments?.getString("userId")
    Home()
}

可选参数可以使用:?argName={argName} 来添加:

composable(
"message?count={count}",
arguments = listOf(navArgument("count") {
    type = NavType.IntType
    defaultValue = 0
})
) { Message() }

5.深层链接

深层链接照搬了官方文档:深层链接

如果你想要将特定的网址、操作或 MIME 类型与导航绑定,实现对外提供跳转应用的功能,那么使用深层链接可以很方便的实现这个功能

url为例,通过deepLinksurl进行绑定:

val uri = "https://www.example.com"

composable(
    "profile?id={id}",
    deepLinks = listOf(navDeepLink { uriPattern = "$uri/{id}" })
) { backStackEntry ->
    Profile(navController, backStackEntry.arguments?.getString("id"))
}

manifest中注册配置:

<activity …>
  <intent-filter>
    ...
    <data android:scheme="https" android:host="www.example.com" />
  </intent-filter>
</activity>

外部通过PendingIntent进行跳转:

val id = "exampleId"
val context = LocalContext.current
val deepLinkIntent = Intent(
    Intent.ACTION_VIEW,
    "https://www.example.com/$id".toUri(),
    context,
    MyActivity::class.java
)

val deepLinkPendingIntent: PendingIntent? = TaskStackBuilder.create(context).run {
    addNextIntentWithParentStack(deepLinkIntent)
    getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT)
}

6.封装导航图

随着业务的越来越复杂,导航图也可能分为模块化,可以在NavHost作用域中使用navigation进行封装:

NavHost(navController, startDestination = "home") {
    ...
    // Navigating to the graph via its route ('login') automatically
    // navigates to the graph's start destination - 'username'
    // therefore encapsulating the graph's internal routing logic
    navigation(startDestination = "username", route = "login") {
        composable("username") { ... }
        composable("password") { ... }
        composable("registration") { ... }
    }
    ...
}

使用扩展函数将更好的对模块进行封装:

fun NavGraphBuilder.loginGraph(navController: NavController) {
    navigation(startDestination = "username", route = "login") {
        composable("username") { ... }
        composable("password") { ... }
        composable("registration") { ... }
    }
}
NavHost(navController, startDestination = "home") {
    ...
    loginGraph(navController)
    ...
}
本文转载于网络 如有侵权请联系删除

相关文章

  • python字符串总结

    参考链接:Python字符串|istitle字符串可以通过切片的方式访问,但是不能对分片进行赋值,字符串是不可变的!  格式化字符串: 格式描述格式描述%%百分号%标记(多出来的%是转义作用)   %c字符及其ASCII码%s字符串%d有符号整数(十进制)%u无符号整数(十进制)%o无符号整数(八进制)   %x无符号整数(十六进制)%X无符号整数(十六进制大写字符)%e浮点数字(科学计数法)%E浮点数字(科学计数法,用E代替e)%f浮点数字(用小数点符号)   %g浮点数字(根据值的不同自动选择%e或%f)%G浮点数字(类似于%g,根据值的不同自动选择%E或%f)%p指针(用十六进制打印值的内存地址)%n存储输出字符的数量放进参数列表的下一个变量中 一个简单的例子:   >>>importmath   >>>pi   3.141592653589793   >>>print'pi=%.5f'%pi   pi=3.14159 如果要在格式化字符串里包括百分号,那么就必须使用 %%,这样python就不会将%误认为

  • 什么,3行Python代码就能获取海量数据?

    一谈起数据分析,首先想到的就是数据,没有数据,谈何分析。 毕竟好的菜肴,没有好的原材料,是很难做的~ 所以本期小F就给大家分享一个获取数据的方法,只需三行代码就能搞定。「GoPUP」,大佬造的轮子,大概有100+的免费数据接口。GitHub:https://github.com/justinzm/gopup使用文档:http://doc.gopup.cn/#/README主要有指数数据、宏观经济数据、新经济数据、微博KOL数据、信息数据、生活数据、疫情数据等。#安装gopup pipinstallgopup--upgrade复制安装成功后,就能使用了。01微博指数获取指定关键词的微博指数。#微博指数 importgopupasgp df_index=gp.weibo_index(word="马保国",time_type="1month") print(df_index)复制time_type="1month";1hour,1day,1month,3month选其一。三行Python代码实现数据获取。 02百度指数获取指定关键词

  • Centreon+Nagios实战第七篇——安装NRPE

    Centreon+Nagios实战第七篇——安装NRPE把以上全部安装完成之后即完成了服务端的监控设置,要想利用服务端监控其他机器的性能,还需要在被禁控端安装NRPE和Nagios-plugin,当然如果要监控本机服务端性能,也需要在本机上安装NRPE。安装NRPE 首要条件 首先确保您已经安装了一下包 gccglibcglibc-commonopenssl-develperl下载安装cd/tmpwgethttps://github.com/NagiosEnterprises/nrpe/archive/nrpe-2-15.tar.gz tarzxvfnrpe-2-15.tar.gz复制拷贝nrpe.cfg文件到您的远程主机nrpe配置目录mkdir/usr/local/nagios/etc cpsample-config/nrpe.cfg/usr/local/nagios/etc复制当NRPE安装完成之后,您需要安装一个初始化系统xinetedyuminstallxinetd复制配置xineted 在这里,我们需要做一个配置老告诉xinted关于nrpe一些信息,转换到nrpesour

  • centos7下解决journal日志越来越大的问题

    背景:线上的机器是使用的ECS,磁盘空间只有40G,而journal日志就占了5G左右,所以要限制其大小和保存时间。介绍:1、查看journal占用磁盘空间大小:journalctl--disk-usage2、指定journal占用磁盘空间大小:journalctl--vacuum-size=1G3、指定journal日志保存时间:journalctl--vacuum-time=1years4、查看具体服务的日志:journalctl-unginx.servicejournalctl-unginx.service-f  实时查看5、实时查看最新日志:journalctl-f6、查看尾部最新的n行日志:journalctl-n207、查看指定时间的日志:journalctl--since"20minago"journalctl--sinceyesterdayjournalctl--since09:00--until"1hourago"8、查看内核日志:journalctl-k9、查看系统启动日志:journalctl-b所以这里我们只需要指定下jo

  • LeetCode - 汉明距离&灯泡开关

    鉴于今天的这两题题解都特别的短,所以把两题写在一起了。分别是461题简单难度的汉明距离和319题中等难度的灯泡开关。原题地址:https://leetcode-cn.com/problems/hamming-distance/和https://leetcode-cn.com/problems/bulb-switcher/题目描述:汉明距离: 两个整数之间的汉明距离指的是这两个数字对应二进制位不同的位置的数目。给出两个整数x和y,计算它们之间的汉明距离。注意:0≤x,y<231.灯泡开关:初始时有n个灯泡关闭。第1轮,你打开所有的灯泡。第2轮,每两个灯泡你关闭一次。第3轮,每三个灯泡切换一次开关(如果关闭则开启,如果开启则关闭)。第i轮,每i个灯泡切换一次开关。对于第n轮,你只切换最后一个灯泡的开关。找出n轮后有多少个亮着的灯泡。来源:力扣(LeetCode)著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。解题思路:汉明距离这题,其实很简单,两个不同数字的二进制位不同位置的数目,利用位运算的异或,可以得到位置不同的二进制位组成的数字N,利用Java的Intege

  • 如何在电脑上保存微信公众号文章封面图片?

    教你如何获取微信公众号的文章封面图片?需求:在微信打开一个公众号,获取公众号文章里面的原图片,右上角标注的图片。这还不简单,直接点到文章里面去,不就可以了?但是点进去之后发现文章里没有插图,没有办法另存为,怎么办?那就复制链接,在网页端打开链接,按f12键查看图片位置和路径,保存到本地啊,历史图文消息排版在网页中打不开,只有某篇具体文章才可以复制黏贴链接。这是我想要的右键单击感觉受到一万点的伤害,那么,今天要分享一个神技能。轻松get到微信公众号的文章封面图片哦。1:点进去这篇只有封面图片的文章,复制文章链接,在浏览器打开。2:右键查看源代码3:按CTRL+F快捷键  ,搜索“varmsg”4:高亮出来的部分代码如下所示:msg_title后面的就是图文消息的标题; msg_desc后面的值是图文消息的摘要内容;msg_cdn_url后面的值是封面图片url;msg_link后面的值是图文消息的链接地址;“msg_cdn_url=”后面的url就是我们要的封面图地址了5:复制粘贴“msg_cdn_url=”后面的url链接在浏览器打开,看到我们想要的这张图片;另存到本地就OK咯。结束,

  • filter 过滤器

    Filter的生命周期包含几个方法 1、构造器方法 2、init初始化方法 第1,2步,在web工程启动的时候执行(Filter已经创建) 3、doFilter过滤方法 第3步,每次拦截到请求,就会执行 4、destroy销毁 第4步,停止web工程的时候,就会执行(停止web工程,也会销毁Filter过滤器) FilterConfig类见名知义,它是Filter过滤器的配置文件类。 Tomcat每次创建Filter的时候,也会同时创建一个FilterConfig类,这里包含了Filter配置文件的配置信息。 FilterConfig类的作用是获取filter过滤器的配置内容 1、获取Filter的名称filter-name的内容 2、获取在Filter中配置的init-param初始化参数 3、获取ServletContext对象上述链的前提是这些都控制的同一个资源  Filter的拦截路径 Filter过滤器它只关心请求的地址是否匹配,不关心请求的资源是否存在!!!

  • 腾讯云物联网智能视频服务(行业版)直播列表接口api接口

    1.接口描述接口请求域名:iotvideoindustry.tencentcloudapi.com。 直播列表接口 默认接口请求频率限制:20次/秒。 APIExplorer提供了在线调用、签名验证、SDK代码生成和快速检索接口等能力。您可查看每次调用的请求内容和返回结果以及自动生成SDK调用示例。 2.输入参数以下请求参数列表仅列出了接口请求参数和部分公共参数,完整公共参数列表见公共请求参数。 参数名称 必选 类型 描述 Action 是 String 公共参数,本接口取值:DescribeLiveChannelList。 Version 是 String 公共参数,本接口取值:2020-12-01。 Region 是 String 公共参数,详见产品支持的地域列表。 Offset 是 Integer 偏移量 Limit 是 Integer 最大数 LiveChannelType 否 Integer 直播频道类型,1:固定直播;2:移动直播 RecordPlanId 否 String 直播录制计划ID,null:直播录制计划为空

  • MySQL删除重复数据

    重复数据如图所示 自关联,保留id最小的那一条,其它的都删除 DELETEt1 FROM invest_yeart1, invest_yeart2 WHERE t1.pro_id=t2.pro_id ANDt1.pro_year=t2.pro_year ANDt1.id>t2.id;复制 DELETE语法如下图所示 Double类型转BigDecimal Doubled=newDouble(99.99); BigDecimalb=newBigDecimal(d.toString()); BigDecimalb1=newBigDecimal("99.99");复制

  • 网易云音乐项目

    0.项目介绍 1.项目后台部署 网易云音乐NodeJS版API文档 后台项目 网易云音乐API 网易云音乐Node.jsAPIservice 灵感来自 disoul/electron-cloud-music darknessomi/musicbox sqaiyan/netmusic-node greats3an/pyncm 环境要求 需要NodeJS8.12+环境 安装 $gitclonegit@github.com:Binaryify/NeteaseCloudMusicApi.git $npminstall 复制 或者 $gitclonehttps://github.com/Binaryify/NeteaseCloudMusicApi.git $npminstall 复制 运行 调用前务必阅读文档的调用前须知 $nodeapp.js 复制 服务器启动默认端口为3000,若不想使用3000端口,可使用以下命令:Mac/Linux $PORT=4000nodeapp.js 复制 windows下使用git-bash或者cmder等终端执行以下命令: $setPORT

  • CDQ分治学习笔记

    upd:整体二分也顺便学了,比较简单不写。 概述 CDQ分治主要用于解决偏序问题。 通常是先按某一维排序,再递归处理分出来的左子问题对右子问题的答案,最后合并。 经典问题-逆序对 同时也是经典的二维偏序问题。 记属性组\((a,b)\),其中\(a\)表示位置,\(b\)表示值。 那么就是求\(a_i<a_j\)且\(b_i>b_j\)的个数。 然后就是对\(a\)排序,对\(b\)树状数组从前往后扫一遍统计即可。 时间复杂度\(\mathcalO(n\logn)\)。 三维偏序 陌上花开 有\(n\)个元素,第\(i\)个元素有\(a_i,b_i,c_i\)三个属性,设\(f(i)\)表示满足\(a_j\leqa_i\)且\(b_j\leqb_i\)且\(c_j\leqc_i\)且\(j\nei\)的\(j\)的数量。 对于\(d\in[0,n)\),求\(f(i)=d\)的数量。 \(1\leqn\leq10^5\),\(1\leqa_i,b_i,c_i\leqk\leq2\times10^5\)。 先按\(a\)维排序,再将左、右子区间按\(b\)维排序。 虽然现在\

  • C#获取当前路径的七种方法

    //1.获取模块的完整路径。 stringpath1=System.Diagnostics.Process.GetCurrentProcess().MainModule.FileName; //2.获取和设置当前目录(该进程从中启动的目录)的完全限定目录 stringpath2=System.Environment.CurrentDirectory; //3.获取应用程序的当前工作目录 stringpath3=System.IO.Directory.GetCurrentDirectory(); //4.获取程序的基目录 stringpath4=System.AppDomain.CurrentDomain.BaseDirectory; //5.获取和设置包括该应用程序的目录的名称 stringpath5=System.AppDomain.CurrentDomain.SetupInformation.ApplicationBase; //6.获取启动了应用程序的可执行文件的路径 stringpath6=System.Windows.Forms.Application.Startu

  • linux中cd / cd~ cd cd- 和cd..之间的区别

    cd    进入用户主目录  cd~   进入用户主目录  cd-   返回进入此目录之前所在目录  cd..   返回上一级目录  cd../.. 返回上两级目录  cd!$  把上个命令的参数作为cd参数使用  cd /   进入根目录 cd .   当前目录  

  • screen命令使用

    screen的主要功能就是在终端下线后,后端执行的命令可以继续。在Screen环境下,所有的会话都独立的运行,并拥有各自的编号、输入、输出和窗口缓存。用户可以通过快捷键在不同的窗口下切换,并可以自由的重定向各个窗口的输入和输出。 用法 screen[-AmRvx-ls-wipe][-d<作业名称>][-h<行数>][-r<作业名称>][-s][-S<作业名称>] 常用选项-s 指定建立新视窗时,所要执行的shell。 -x 加入已有的sreen作业或者恢复之前离线的screen作业。 -r<作业名称> 恢复离线的screen作业。 -ls或--list 显示目前所有的screen作业。 其他选项-A 将所有的视窗都调整为目前终端机的大小。-d<作业名称> 将指定的screen作业离线。-h<行数> 指定视窗的缓冲区行数。-m 即使目前已在作业中的screen作业,仍强制建立新的screen作业。-R 先试图恢复离线的作业。若找不到离线的作业,即建立新的screen作业。-S<作业名称> 指定

  • LOOP GROUP BY 分组循环的使用方法小栗子

    原文链接:https://blog.csdn.net/lmf496891416/article/details/111317377 1.格式: LOOPAT内表INTODATA(工作区)GROUPBY(字段=工作区-字段 size=GROUPSIZE"分组组数-- index=GROUPINDEX)"分组的组序号 ASCENDINGASSIGNINGFIELD-SYMBOL(<组>). LOOPATGROUP<组>ASSIGNINGFIELD-SYMBOL(<指针>). "对指针进行操作,和普通的loop循环相似 ENDLOOP. ENDLOOP.复制 2.简而言之:就是第一个loop循环是把内表进行分组,第二loop循环是把组里的数据循环读出来进行修改判断等操作。3.可以替代ATENDOF/ATNEWOF。4.具体实例-复制就能用 TYPES:BEGINOFty_employee, nameTYPEchar30, roleTYPEchar30, ageTYPEi, sexTYPEchar10, ENDOFty_employee, ty_e

  • 四、鸢尾花分类

      下载地址:http://t.cn/Rf8GeUq 先安装sklearn库,anaconda在paddle环境中搜索scikit-learn,安装即可。 importnumpyasnp frommatplotlibimportcolors#画图 fromsklearnimportsvm fromsklearn.svmimportSVC fromsklearnimportmodel_selection importmatplotlib.pyplotasplt importmatplotlibasmpl defiris_type(s): it={b'Iris-setosa':0,b'Iris-versicolor':1,b'Iris-virginica':2} returnit[s] #加载数据 data_path='D:/home/aistudio/data/data5420/iris.data'#数据文件的路径 data=np.loadtxt(data_path,#数据文件路径 dtype=float,#数据类型 delimiter=',',#数据分隔符

  • #word# word2013去除脚注的空白行

    在word2013添加脚注时,会出现多余空白行,删除空白行操作如下: 1.点击“视图-草稿”; 2.“引用-显示备注”跳转到脚注备注区; 3.下拉菜单中点击“脚注分隔符”,删除多余空白行即可。

  • 什么?又是自查表问卷?

    真的很费脑筋,简易在写逻辑的时候找个清净点的地方  废话不多说 直接撸代码 <%@PageLanguage="C#"AutoEventWireup="true"CodeFile="jsxx_ckjgNew.aspx.cs"Inherits="wjtwh_Default"%> <!DOCTYPEhtml> <htmllang="zh-cn"> <headrunat="server"> <metacharset="UTF-8"> <metaname="viewport"content="width=device-width,initial-scale=1.0"> <title>上城区自查表模板(2020年06月份)</title> <linkrel="stylesheet"type="text/css"href="../css/css.css"/> <scriptsrc="../js/jquery-1.9.1.min.js"type="text

  • 黄金点游戏

       N个同学(N通常大于10),每人写一个0~100之间的有理数 (不包括0或100),交给裁判,裁判算出所有数字的平均值,然后乘以0.618(所谓黄金分割常数),得到G值。提交的数字最靠近G(取绝对值)的同学得到N分,离G最远的同学得到-2分,其他同学得0分。玩了几天以后,大家发现了一些很有意思的现象,比如黄金点在逐渐地往下移动。    现在请大家根据这个游戏规则,编一个可以多人一起玩的小游戏程序,要求如下:    1、本作业属于结对编程项目,必须由二人共同完成,并分别将本次作业过程发到博客,同时将本次作业源代码提交到codeing系统;    2、如果可能的话尽量以C/S或B/S方式实现,即利用服务器接收和处理所有玩家提交的数字,并将结果反馈给各玩家,玩家可以通过客户端提交的数字;    3、如果采用单机方式实现的话,需要为用户提供便利的输入界面;    4、该游戏每次至少可以运行10轮以上,

  • 一个由Mysql特性引起的php验证漏洞

    某php+mysql系统,mysql数据库中有个user表,其中有个code字段,类型是int(11),这个字段是保存一个随机数,用来找回密码的时候做验证,默认值是0。   找回密码时候的步骤是,首先填写自己邮箱,接收重置密码的邮件,点击链接,访问如下代码: if(!empty($_GET['email'])&&!empty($_GET['code'])) {     if(!$db->count('user',"email='{$_GET['email']}'ANDcode='{$_GET['code']}'"))         die('error');     $_SESSION['email']=$_GET['email'];     ... }   在数据库中查找email=$

  • 第3章 代码检查、走查与评审

    代码检查与走查 代码检查、走查以及可用性测试是三种主要的人工测试方法。人工测试技术在查找错误方面非常有效,应该在程序开始编码之后、基于计算机的测试开始之前使用这些方法。本章主要介绍的是代码检查与走查的相似之处,而它们的不同之处将在后续章节中介绍。 代码检查与走查都要求人们组成一个小组(一场"头脑风暴会")来阅读或直观检查特定的程序。 "头脑风暴会"的目标是找出错误来,但不必找出改正错误的方法。在代码走查中,一组开发人员(三到四人为最佳)对代码进行审核。其中 只有一人是代码的作者。因此,代码走查的主要工作是由其他人,而不是作者本人完成的,这和软件测试的第原则,也即"软件编写者往往不能有效地测试自己的软件"相符合。 代码检查与走查是对过去桌面检查过程(在提交测试前由程序员阅读自己程序的过程)的改进。因为在实施过程中,除了软件编写者本人,还有其他人参与进来。代码走查的另一个优点在于,一旦发现错误,通常就能在代码中对其进行精确定位,这就降低了调试(错误修正)的成本。在典型的程序中,这些方陆通常会有效地查找出30%-70%(这些方陆在测试过程结束时可以有效地查找出多达70%的已知错误)的逻辑设计

相关推荐

推荐阅读