Android 点击通知栏消息打开activity,并判断app是否运行

简介: Android 点击通知栏消息打开activity,并判断app是否运行

android的通知栏消息点击事件如果是打开一个activity时,我们要考虑两种情况:


应用正在前台运行。

应用已退出。

如果是第一种情况那么就好处理了,直接为Intent设置flag为FLAG_ACTIVITY_NEW_TASK,然后调用context.startActivity方法就行了。flag不是必须的,什么情况下需要设置flag?当在广播接收器中跳转到activity时,当在service中转到activity时。


对于第二种情况,我参照了很多app的做法,现总结为以下两种:


点击通知栏消息打开activity按下返回键后判断app是否启动,如果没有启动就要启动app;

点击通知栏消息时判断app是否正在前台运行,否则先启动app再打开activity,参数通过Intent一层一层往下传递。

需要用到几个方法:获取应用的运行状态,判断应用进程是否在运行,判断某个activity是否存在任务栈里面。


判断某个服务是否正在运行,这个不重要,可能其它地方用到就先贴出来了。 ==


/**
   * 判断某个service是否正在运行
   * 
   * @param context
   * @param runService
   *            要验证的service组件的类名
   * @return 
   */
  public static boolean isServiceRunning(Context context,
      Class<? extends Service> runService) {
    ActivityManager am = (ActivityManager) context
        .getSystemService(Context.ACTIVITY_SERVICE);
    ArrayList<RunningServiceInfo> runningService = (ArrayList<RunningServiceInfo>) am
        .getRunningServices(1024);
    for (int i = 0; i < runningService.size(); ++i) {
      if (runService.getName().equals(
          runningService.get(i).service.getClassName().toString())) {
        return true;
      }
    }
    return false;
  }

获取app的运行状态,返回1代表当前应用在前台运行,返回2代表当前应用在后台运行,返回0代表应用未启动(没有一个存活的activity)。

/**
   * 返回app运行状态
   * 
   * @param context
   *            一个context
   * @param packageName
   *            要判断应用的包名
   * @return int 1:前台 2:后台 0:不存在
   */
  public static int isAppAlive(Context context, String packageName) {
    ActivityManager activityManager = (ActivityManager) context
        .getSystemService(Context.ACTIVITY_SERVICE);
    List<ActivityManager.RunningTaskInfo> listInfos = activityManager
        .getRunningTasks(20);
    // 判断程序是否在栈顶
    if (listInfos.get(0).topActivity.getPackageName().equals(packageName)) {
      return 1;
    } else {
      // 判断程序是否在栈里
      for (ActivityManager.RunningTaskInfo info : listInfos) {
        if (info.topActivity.getPackageName().equals(packageName)) {
          return 2;
        }
      }
      return 0;// 栈里找不到,返回3
    }
  }

判断某个进程是否运行

/**
   * 判断进程是否运行
   * 
   * @param context
   * @param proessName 应用程序的主进程名一般为包名
   * @return
   */
  public static boolean isProessRunning(Context context, String proessName) {
    boolean isRunning = false;
    ActivityManager am = (ActivityManager) context
        .getSystemService(Context.ACTIVITY_SERVICE);
    List<RunningAppProcessInfo> lists = am.getRunningAppProcesses();
    for (RunningAppProcessInfo info : lists) {
      if (info.processName.equals(proessName)) {
        isRunning = true;
      }
    }
    return isRunning;
  }

判断某个activity是否在任务栈里面,app启动后会有一个首页,该首页只有当app退出时才会被销毁,因此可用判断MainActivity是否在任务栈里面来判断应用是否已经启动。

/**
   * 判断某一个类是否存在任务栈里面
   * 
   * @return
   */
  public static boolean isExsitMianActivity(Context context, Class<?> cls) {
    Intent intent = new Intent(context, cls);
    ComponentName cmpName = intent.resolveActivity(context
        .getPackageManager());
    boolean flag = false;
    if (cmpName != null) { // 说明系统中存在这个activity
      ActivityManager am = (ActivityManager) context
          .getSystemService(Context.ACTIVITY_SERVICE);
      List<RunningTaskInfo> taskInfoList = am.getRunningTasks(10);
      for (RunningTaskInfo taskInfo : taskInfoList) {
        if (taskInfo.baseActivity.equals(cmpName)) { // 说明它已经启动了
          flag = true;
          break; // 跳出循环,优化效率
        }
      }
    }
    return flag;
  }

接下来是第一种方法的实现:

在需要跳转的activity中或BaseActivity中的onCreate方法中获取intent传递过来的数据,判断是否是从点击通知栏消息跳转过来,并用一个字段保存这个状态,再处理相应的逻辑业务。

private int isNoticeOpen = 0;// 是否是点击消息通知跳转进来的
 ```
 ```java
@Override
 public void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  Bundle bun = getIntent().getExtras();
  if (bun != null) {
    // 判断是否是消息通知点击跳转进行的
    try{
      isNoticeOpen = Integer.valueOf(bun.getString("NOTICE"));
    }catch(NumberFormatException e){
      isNoticeOpen = 0;
      e.printStackTrace();
    }
  }...............获取其它通知传递过来的参数...........
 }
在onDestroy方法中判断该应用是否正在前台运行,但是这里只能用MainActivity是否存在任务栈里面判断,因为当你点击通知消息跳转到某个activity的时候,任务栈里该activity就处于栈顶了,而栈顶的activity的包名就是该应用的包名。
    @Override
      public void onDestroy() {
        super.onDestroy();
        //如果是点击消息跳转进来的,且(该运行的进程里没有该应用进程 或 应用首页的Activity不存在任务栈里面)
        if (isNoticeOpen==1&&
            (!ServiceHelper.isProessRunning(getApplicationContext(),this.getPackageName())
                ||!ServiceHelper.isExsitMianActivity(this,MainActivity_.class))) {
          //启动app
          Intent intent = getBaseContext().getPackageManager()
              .getLaunchIntentForPackage(getPackageName());
          intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
          startActivity(intent);
        }
      }

最后是第二种实现方式


在点击通知消息时就判断应用是否在前台运行,没有就启动应用。这里用到了一个ServiceHelper类,这个类是我写的,为了简写跳转过程而封装一些步骤的类。

1.处理通知消息点击事件跳转到某个页面,这里的Intent并没有设置要跳转到的activity,而是将要跳转到的activity当作Intent的参数。

    /**
   * 处理通知消息点击事件跳转到指定界面
   * 
   * @param msg
   */
  private void handerMsgCallToActivity(Context context, UMessage msg) {
    ......................................................
    Intent intent = new Intent();
    /**
     * 兼容友盟后台直接推送的
     */
    // 获取动作参数
    String actionKey = msg.extra.get("action");
    if (null == actionKey)
      return;
    if ("message".equals(actionKey)) {
      intent.putExtra(UmenPushManager.CALL_TO_ACTIVITY,
          MessageActivity_.class);
      intent.putExtra("NOTICE", true);
      intent.putExtra("msgCount", 999);// 大于0即可
    } else if ("news".equals(actionKey)) {
      String newtype = msg.extra.get("newstype");
      String newsId = msg.extra.get("nid");
      String newsTitle = msg.extra.get("ntitle");
      String newsUrl = msg.extra.get("nurl");
      ..............................
      intent.putExtra(UmenPushManager.CALL_TO_ACTIVITY,DetailsActivity_.class);
      intent.putExtra("NOTICE", true);
      intent.putExtra("news_id", newsId);
      intent.putExtra("url", newsUrl);
    } else if ("outlink".equals(actionKey)) {
      String title = msg.extra.get("title");
      String url = msg.extra.get("url");
      intent.putExtra(UmenPushManager.CALL_TO_ACTIVITY,
          BrowserActivity_.class);
      intent.putExtra("title", title);
      intent.putExtra("url", url);
    } 
    ServiceHelper.startActivityWithAppIsRuning(context, intent);
  }

2.上一步中只是获取并设置页面跳转中要传递的数据并指定了要跳转到哪个页面,而真正的跳转任务交给了ServiceHelper类的startActivityWithAppIsRuning方法实现。在startActivityWithAppIsRuning方法中进行判断应用是否在运行,没有则创建一个Intent,设置跳转目标Activity,该Activity由上一步传过来的Intent获取到。否则就启动应用,intent中传递一个键为FORM_NOTICE_OPEN,值为true的参数标识是从点击消息通知跳转过来的,再将上一步传递过来的intent当做参数传给当前的intent。


    /**
       * 自动判断appUI进程是否已在运行,设置跳转信息
       * 
       * @param context
       * @param intent
       */
      public static void startActivityWithAppIsRuning(Context context,
          Intent intent) {
        int isAppRuning = isAppAlive(context, UmenPushManager.APP_PACKAGE);
        if (isAppRuning != 0) {
          Intent newIntent = new Intent(context, (Class<?>) intent
              .getExtras().getSerializable(
                  UmenPushManager.CALL_TO_ACTIVITY));
          newIntent.putExtras(intent.getExtras());
          newIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
          context.startActivity(newIntent);
          return;
        }
        // 如果app进程已经被杀死,先重新启动app,将DetailActivity的启动参数传入Intent中,参数经过
        // SplashActivity传入MainActivity,此时app的初始化已经完成,在MainActivity中就可以根据传入
        // 参数跳转到DetailActivity中去了
        Intent launchIntent = context.getPackageManager()
            .getLaunchIntentForPackage(UmenPushManager.APP_PACKAGE);
        launchIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
            | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
        launchIntent.putExtra(UmenPushManager.FORM_NOTICE_OPEN, true);
        launchIntent.putExtra(UmenPushManager.FORM_NOTICE_OPEN_DATA, intent);
        context.startActivity(launchIntent);
      }

3.在应用的启动页中不做处理,直接传递给MainActivity,首先是在启动页WelcomeActivity中调用ServiceHelper类的startAppMainActivitySetNoticeIntent方法判断是否从点击通知消息跳转过来,如果是则为跳转到MainActivity的Intent写入传递过来的数据。


   //如果是点击通知打开的则设置通知参数

           ServiceHelper.startAppMainActivitySetNoticeIntent(this, intent);


    /**
       * 启动App时,为跳转到主页MainActivity的Intent写入打开通知的Intent,如果有通知的情况下
       * 
       * @param appStartActivity
       *            app启动的第一个activity,在配置文件中设置的mainactivity
       * @param startMainActivityIntent
       */
      public static void startAppMainActivitySetNoticeIntent(
          Activity appStartActivity, Intent startMainActivityIntent) {
        /**
         * 如果启动app的Intent中带有额外的参数,表明app是从点击通知栏的动作中启动的 将参数取出,传递到MainActivity中
         */
        try {
          if (appStartActivity.getIntent().getExtras() != null) {
            if (appStartActivity.getIntent().getExtras()
                .getBoolean(UmenPushManager.FORM_NOTICE_OPEN) == true) {
              startMainActivityIntent
                  .putExtra(
                      UmenPushManager.FORM_NOTICE_OPEN_DATA,
                      appStartActivity
                          .getIntent()
                          .getExtras()
                          .getParcelable(
                              UmenPushManager.FORM_NOTICE_OPEN_DATA));
            }
          }
        } catch (Exception e) {
        }
      }

4.在MainActivity的onCreate中调用


    /**

       * 如果是从点击通知栏的通知跳转过来的

       */

      ServiceHelper.isAppWithNoticeOpen(this);

1

2

3

4

再看ServiceHelper的isAppWithNoticeOpen方法。

    /**
       * 判断是否是点击消息通知栏跳转过来的
       * 
       * @param mainActivity
       *            主页
       */
      public static void isAppWithNoticeOpen(Activity mainActivity) {
        try {
          if (mainActivity.getIntent().getExtras() != null) {
            Intent intent = mainActivity.getIntent().getExtras()
                .getParcelable(UmenPushManager.FORM_NOTICE_OPEN_DATA);
            Intent newIntent = new Intent(mainActivity, (Class<?>) intent
                .getExtras().getSerializable(
                    UmenPushManager.CALL_TO_ACTIVITY));
            newIntent.putExtras(intent.getExtras());
            mainActivity.startActivity(newIntent);
          }
        } catch (Exception e) {
        }
      }

最关键的一点时,到这一步才处理点击通知消息真正要跳转到的页面。(Class<?>) intent .getExtras().getSerializable( UmenPushManager.CALL_TO_ACTIVITY)获取到的是需要跳转到的页面,其它数据原封不动往下传递过去就行了。

最后附上完整的ServiceHelper类:

/**
 * 后台service组件助手
 * 
 * @author wujiuye
 * 
 */
public final class ServiceHelper {
  /**
   * 判断某个service是否正在运行
   * 
   * @param context
   * @param runService
   *            要验证的service组件的类名
   * @return 
   */
  public static boolean isServiceRunning(Context context,
      Class<? extends Service> runService) {
    ActivityManager am = (ActivityManager) context
        .getSystemService(Context.ACTIVITY_SERVICE);
    ArrayList<RunningServiceInfo> runningService = (ArrayList<RunningServiceInfo>) am
        .getRunningServices(1024);
    for (int i = 0; i < runningService.size(); ++i) {
      if (runService.getName().equals(
          runningService.get(i).service.getClassName().toString())) {
        return true;
      }
    }
    return false;
  }
  /**
   * 返回app运行状态
   * 
   * @param context
   *            一个context
   * @param packageName
   *            要判断应用的包名
   * @return int 1:前台 2:后台 0:不存在
   */
  public static int isAppAlive(Context context, String packageName) {
    ActivityManager activityManager = (ActivityManager) context
        .getSystemService(Context.ACTIVITY_SERVICE);
    List<ActivityManager.RunningTaskInfo> listInfos = activityManager
        .getRunningTasks(20);
    // 判断程序是否在栈顶
    if (listInfos.get(0).topActivity.getPackageName().equals(packageName)) {
      return 1;
    } else {
      // 判断程序是否在栈里
      for (ActivityManager.RunningTaskInfo info : listInfos) {
        if (info.topActivity.getPackageName().equals(packageName)) {
          return 2;
        }
      }
      return 0;// 栈里找不到,返回3
    }
  }
  /**
   * 自动判断appUI进程是否已在运行,设置跳转信息
   * 
   * @param context
   * @param intent
   */
  public static void startActivityWithAppIsRuning(Context context,
      Intent intent) {
    int isAppRuning = isAppAlive(context, UmenPushManager.APP_PACKAGE);
    if (isAppRuning != 0) {
      Intent newIntent = new Intent(context, (Class<?>) intent
          .getExtras().getSerializable(
              UmenPushManager.CALL_TO_ACTIVITY));
      newIntent.putExtras(intent.getExtras());
      newIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
      context.startActivity(newIntent);
      return;
    }
    // 如果app进程已经被杀死,先重新启动app,将DetailActivity的启动参数传入Intent中,参数经过
    // SplashActivity传入MainActivity,此时app的初始化已经完成,在MainActivity中就可以根据传入
    // 参数跳转到DetailActivity中去了
    Intent launchIntent = context.getPackageManager()
        .getLaunchIntentForPackage(UmenPushManager.APP_PACKAGE);
    launchIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
        | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
    launchIntent.putExtra(UmenPushManager.FORM_NOTICE_OPEN, true);
    launchIntent.putExtra(UmenPushManager.FORM_NOTICE_OPEN_DATA, intent);
    context.startActivity(launchIntent);
  }
  /**
   * 启动App时,为跳转到主页MainActivity的Intent写入打开通知的Intent,如果有通知的情况下
   * 
   * @param appStartActivity
   *            app启动的第一个activity,在配置文件中设置的mainactivity
   * @param startMainActivityIntent
   */
  public static void startAppMainActivitySetNoticeIntent(
      Activity appStartActivity, Intent startMainActivityIntent) {
    /**
     * 如果启动app的Intent中带有额外的参数,表明app是从点击通知栏的动作中启动的 将参数取出,传递到MainActivity中
     */
    try {
      if (appStartActivity.getIntent().getExtras() != null) {
        if (appStartActivity.getIntent().getExtras()
            .getBoolean(UmenPushManager.FORM_NOTICE_OPEN) == true) {
          startMainActivityIntent
              .putExtra(
                  UmenPushManager.FORM_NOTICE_OPEN_DATA,
                  appStartActivity
                      .getIntent()
                      .getExtras()
                      .getParcelable(
                          UmenPushManager.FORM_NOTICE_OPEN_DATA));
        }
      }
    } catch (Exception e) {
    }
  }
  /**
   * 判断是否是点击消息通知栏跳转过来的
   * 
   * @param mainActivity
   *            主页
   */
  public static void isAppWithNoticeOpen(Activity mainActivity) {
    try {
      if (mainActivity.getIntent().getExtras() != null) {
        Intent intent = mainActivity.getIntent().getExtras()
            .getParcelable(UmenPushManager.FORM_NOTICE_OPEN_DATA);
        Intent newIntent = new Intent(mainActivity, (Class<?>) intent
            .getExtras().getSerializable(
                UmenPushManager.CALL_TO_ACTIVITY));
        newIntent.putExtras(intent.getExtras());
        mainActivity.startActivity(newIntent);
      }
    } catch (Exception e) {
    }
  }
  /**
   * 判断进程是否运行
   * 
   * @param context
   * @param proessName
   * @return
   */
  public static boolean isProessRunning(Context context, String proessName) {
    boolean isRunning = false;
    ActivityManager am = (ActivityManager) context
        .getSystemService(Context.ACTIVITY_SERVICE);
    List<RunningAppProcessInfo> lists = am.getRunningAppProcesses();
    for (RunningAppProcessInfo info : lists) {
      if (info.processName.equals(proessName)) {
        isRunning = true;
      }
    }
    return isRunning;
  }
  /**
   * 判断某一个类是否存在任务栈里面
   * 
   * @return
   */
  public static boolean isExsitMianActivity(Context context, Class<?> cls) {
    Intent intent = new Intent(context, cls);
    ComponentName cmpName = intent.resolveActivity(context
        .getPackageManager());
    boolean flag = false;
    if (cmpName != null) { // 说明系统中存在这个activity
      ActivityManager am = (ActivityManager) context
          .getSystemService(Context.ACTIVITY_SERVICE);
      List<RunningTaskInfo> taskInfoList = am.getRunningTasks(10);
      for (RunningTaskInfo taskInfo : taskInfoList) {
        if (taskInfo.baseActivity.equals(cmpName)) { // 说明它已经启动了
          flag = true;
          break; // 跳出循环,优化效率
        }
      }
    }
    return flag;
  }
}

** 最后想在结尾简单的提一下Activity的四种加载模式:**


1.standard:Activity的默认加载方法,即使某个Activity在Task栈中已经存在,另一个activity通过Intent跳转到该activity,同样会新创建一个实例压入栈中。例如:现在栈的情况为:A B C D,在D这个Activity中通过Intent跳转到D,那么现在的栈情况为: A B C D D 。此时如果栈顶的D通过Intent跳转到B,则栈情况为:A B C D D B。此时如果依次按返回键,D D C B A将会依次弹出栈而显示在界面上。


2.singleTop:如果某个Activity的Launch mode设置成singleTop,那么当该Activity位于栈顶的时候,再通过Intent跳转到本身这个Activity,则将不会创建一个新的实例压入栈中。例如:现在栈的情况为:A B C D。D的Launch mode设置成了singleTop,那么在D中启动Intent跳转到D,那么将不会新创建一个D的实例压入栈中,此时栈的情况依然为:A B C D。但是如果此时B的模式也是singleTop,D跳转到B,那么则会新建一个B的实例压入栈中,因为此时B不是位于栈顶,此时栈的情况就变成了:A B C D B。


3.singleTask:如果某个Activity是singleTask模式,那么Task栈中将会只有一个该Activity的实例。例如:现在栈的情况为:A B C D。B的Launch mode为singleTask,此时D通过Intent跳转到B,则栈的情况变成了:A B。而C和D被弹出销毁了,也就是说位于B之上的实例都被销毁了。


4.singleInstance:将Activity压入一个新建的任务栈中。例如:Task栈1的情况为:A B C。C通过Intent跳转到D,而D的Launch mode为singleInstance,则将会新建一个Task栈2。此时Task栈1的情况还是为:A B C。Task栈2的情况为:D。此时屏幕界面显示D的内容,如果这时D又通过Intent跳转到D,则Task栈2中也不会新建一个D的实例,所以两个栈的情况也不会变化。而如果D跳转到C,则栈1的情况变成了:A B C C,因为C的Launch mode为standard,此时如果再按返回键,则栈1变成:A B C。也就是说现在界面还显示C的内容,不是D。


目录
相关文章
|
3月前
|
XML Java 数据库
安卓项目:app注册/登录界面设计
本文介绍了如何设计一个Android应用的注册/登录界面,包括布局文件的创建、登录和注册逻辑的实现,以及运行效果的展示。
287 0
安卓项目:app注册/登录界面设计
|
15天前
|
Dart 前端开发 Android开发
【02】写一个注册页面以及配置打包选项打包安卓apk测试—开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草央千澈
【02】写一个注册页面以及配置打包选项打包安卓apk测试—开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草央千澈
【02】写一个注册页面以及配置打包选项打包安卓apk测试—开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草央千澈
|
1月前
|
存储 监控 API
app开发之安卓Android+苹果ios打包所有权限对应解释列表【长期更新】-以及默认打包自动添加权限列表和简化后的基本打包权限列表以uniapp为例-优雅草央千澈
app开发之安卓Android+苹果ios打包所有权限对应解释列表【长期更新】-以及默认打包自动添加权限列表和简化后的基本打包权限列表以uniapp为例-优雅草央千澈
|
4月前
|
存储 开发工具 Android开发
使用.NET MAUI开发第一个安卓APP
【9月更文挑战第24天】使用.NET MAUI开发首个安卓APP需完成以下步骤:首先,安装Visual Studio 2022并勾选“.NET Multi-platform App UI development”工作负载;接着,安装Android SDK。然后,创建新项目时选择“.NET Multi-platform App (MAUI)”模板,并仅针对Android平台进行配置。了解项目结构,包括`.csproj`配置文件、`Properties`配置文件夹、平台特定代码及共享代码等。
406 2
|
4月前
|
XML Android开发 数据格式
🌐Android国际化与本地化全攻略!让你的App走遍全球无障碍!🌍
在全球化背景下,实现Android应用的国际化与本地化至关重要。本文以一款旅游指南App为例,详细介绍如何通过资源文件拆分与命名、适配布局与方向、处理日期时间及货币格式、考虑文化习俗等步骤,完成多语言支持和本地化调整。通过邀请用户测试并收集反馈,确保应用能无缝融入不同市场,提升用户体验与满意度。
166 3
|
3月前
|
安全 网络安全 Android开发
深度解析:利用Universal Links与Android App Links实现无缝网页至应用跳转的安全考量
【10月更文挑战第2天】在移动互联网时代,用户经常需要从网页无缝跳转到移动应用中。这种跳转不仅需要提供流畅的用户体验,还要确保安全性。本文将深入探讨如何利用Universal Links(仅限于iOS)和Android App Links技术实现这一目标,并分析其安全性。
523 0
|
4月前
|
XML 数据库 Android开发
10分钟手把手教你用Android手撸一个简易的个人记账App
该文章提供了使用Android Studio从零开始创建一个简单的个人记账应用的详细步骤,包括项目搭建、界面设计、数据库处理及各功能模块的实现方法。
|
8月前
|
数据库 Android开发 开发者
Android基础知识:请解释Activity的生命周期。
Android基础知识:请解释Activity的生命周期。
83 2
|
7月前
|
Android开发 UED
Android Activity的生命周期详解
Android Activity的生命周期详解
105 0
|
8月前
|
Android开发
Android Studio APP开发入门之活动Activity中启停活动页面的讲解及实战(附源码,包括Activity的启动结束、生命周期、跳转等)
Android Studio APP开发入门之活动Activity中启停活动页面的讲解及实战(附源码,包括Activity的启动结束、生命周期、跳转等)
167 0

热门文章

最新文章

  • 1
    DeepSeek Artifacts:在线实时预览的前端 AI 编程工具,基于DeepSeek V3快速生成React App
  • 2
    圈子社交app前端+后端源码,uniapp社交兴趣圈子开发,框架php圈子小程序安装搭建
  • 3
    【05】2025年1月首发完整版-篇幅较长-苹果app如何上架到app store完整流程·不借助第三方上架工具的情况下无需花钱但需仔细学习-优雅草央千澈详解关于APP签名以及分发-们最关心的一篇来了-IOS上架app
  • 4
    【05】flutter完成注册页面完善样式bug-增加自定义可复用组件widgets-严格规划文件和目录结构-规范入口文件-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草央千澈
  • 5
    电竞陪玩系统架构优化设计,陪玩app如何提升系统稳定性,陪玩小程序平台的测试与监控
  • 6
    京东商品详情数据接口(H5、APP 端)
  • 7
    年轻人如何运用圈子系统进行扩列,社交圈子论坛app扩列的好处,兴趣行业圈子提升社交技能
  • 8
    【Azure App Service】对App Service中CPU指标数据中系统占用部分(System CPU)的解释
  • 9
    【Azure Logic App】使用MySQL 新增行触发器遇见错误 :“Unknown column 'created_at' in 'order clause'”
  • 10
    【01】vs-code如何配置flutter环境-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草央千澈-供大大的学习提升