【问题标题】:Can't Update Notifications text after Forerground service init; Context issue前台服务初始化后无法更新通知文本;上下文问题
【发布时间】:2018-08-18 22:39:41
【问题描述】:

我有一个简单的 NanoHTTPD 服务器作为前台服务运行。

当收到对服务器的新请求时,我在使用新内容更新通知时遇到问题。

前台服务启动并显示通知。那里没有问题。但以后无法更新。

文件结构
- 主要活动
- NanoServer (服务器实现)
- NanoService (前台服务类)
- NotificationProvider (处理通知的单独类)

NanoServer.java

    public Context context = getContext();
    public NotificationProvider notificationProvider;

    public NanoServer(int port) {
        super(8089);
    }

    @Override
    public Response serve(String uri, Method method,
                          Map<String, String> header, Map<String, String> parameters,
                          Map<String, String> files) {
        String answer = "";
        String msg;

        // doesnt work with the context. something wrong here I guess????
        notificationProvider = new NotificationProvider();
        notificationProvider.setNotification(context, "Title", uri, 0);

        FileInputStream fis = null;

        try {
            fis = new FileInputStream(uri);
            Log.w(TAG, uri + " found");
        } catch (FileNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return newChunkedResponse(Status.OK, "audio/mpeg", fis);

    }

    public Context getContext() {
        return context;
    }

NanoService.java

    String TAG = "NANOSERVICE";
    public Context context = this;
    public Handler handler = null;
    public static Runnable runnable = null;
    PowerManager powerManager;
    PowerManager.WakeLock wakeLock;
    WifiManager.WifiLock wifiLock;

    private NanoServer nanoServer;
    public NotificationProvider notificationProvider;

    public NanoService() {
    }

    @Override
    public IBinder onBind(Intent intent) {
        // TODO: Return the communication channel to the service.
        throw new UnsupportedOperationException("Not yet implemented");
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        // Start the httpd.
        try {
            nanoServer = new NanoServer(8089);
            nanoServer.start();
            Log.d(TAG, "Service with server started");
        } catch (IOException e) {
            e.printStackTrace();
            Toast.makeText(this, "Service failed to start.", Toast.LENGTH_LONG).show();
        }

        // Keep the CPU awake (but not the screen).
        powerManager = (PowerManager)getSystemService(Context.POWER_SERVICE);
        wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
        wakeLock.acquire();

        // Keep the WIFI turned on.
        WifiManager wm = (WifiManager)context.getSystemService(Context.WIFI_SERVICE);
        wifiLock = wm.createWifiLock(WifiManager.WIFI_MODE_FULL_HIGH_PERF, TAG);
        wifiLock.acquire();

        notificationProvider = new NotificationProvider();
        notificationProvider.setNotification(this, "Title", "Message", 0);

        // had to extend notificationprovider with notification
        startForeground(1, notificationProvider);
        Log.d(TAG, "Foreground service running");

        return Service.START_STICKY;

    }

    @Override
    public void onDestroy() {
        stopForeground(true);
        wakeLock.release();
        wifiLock.release();
        nanoServer.stop();
    }

NotificationProvider.java

public class NotificationProvider extends Notification {
    String TAG = "NOTIFICATIONPROVIDER";
    public NotificationProvider() {
    }

    public void setNotification(Context context, String notificationTitle, String notificationMessage, int notificationRequestCode){
        NotificationCompat.Builder builder =
                new NotificationCompat.Builder(context)
                        .setSmallIcon(R.drawable.ic_launcher_background)
                        .setContentTitle(notificationTitle)
                        .setContentText(notificationMessage)
                        .setTicker("My service")
                        .setColor(101)
                        .setWhen(System.currentTimeMillis())
                        .setOngoing(true)
                        .setPriority(NotificationCompat.PRIORITY_MAX);

        Intent intent = new Intent(context, MainActivity.class);
        PendingIntent contentIntent = PendingIntent.getActivity(context, notificationRequestCode, intent, PendingIntent.FLAG_UPDATE_CURRENT);
        builder.setContentIntent(contentIntent);

        NotificationManager manager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
        manager.notify(0, builder.build());
        Log.d(TAG, "Got new Notification");
    }
}

【问题讨论】:

    标签: java android android-notifications nanohttpd foreground-service


    【解决方案1】:

    我认为最简单的解决方案是使用相同的构建器方法来更新通知。

    使用更新后的NotificationProvider

    NanoService(或其他任何地方)中将new NotificationProvider() 更改为NotificationProvider.getInstance()

    import android.app.Notification;
    import android.app.NotificationManager;
    import android.app.PendingIntent;
    import android.content.Context;
    import android.content.Intent;
    import android.support.v4.app.NotificationCompat;
    import android.util.Log;
    
    import com.iroid.videoeditor.R;
    import com.iroid.videoeditor.main.MainActivity;
    
    public class NotificationProvider extends Notification {
    
        private static NotificationProvider sInstance;
    
        private NotificationCompat.Builder mBuilder;
        private String TAG = "NOTIFICATIONPROVIDER";
    
        public static NotificationProvider getInstance() {
            if (sInstance == null)
                sInstance = new NotificationProvider();
    
            return sInstance;
        }
    
        // Prevent creating new instances from outside
        private NotificationProvider() {
        }
    
        public void setNotification(Context context, String notificationTitle, String
                notificationMessage, int notificationRequestCode) {
    
            NotificationManager manager = (NotificationManager) context.getSystemService(Context
                    .NOTIFICATION_SERVICE);
    
            if (mBuilder == null) {
                // Notification doesn't exists. Need to create one.
                mBuilder =
                        new NotificationCompat.Builder(context)
                                .setSmallIcon(R.drawable.ic_launcher_background)
                                .setContentTitle(notificationTitle)
                                .setContentText(notificationMessage)
                                .setTicker("My service")
                                .setColor(101)
                                .setWhen(System.currentTimeMillis())
                                .setOngoing(true)
                                .setPriority(NotificationCompat.PRIORITY_MAX);
    
                Intent intent = new Intent(context, MainActivity.class);
                PendingIntent contentIntent = PendingIntent.getActivity(context,
                        notificationRequestCode, intent, PendingIntent.FLAG_UPDATE_CURRENT);
                mBuilder.setContentIntent(contentIntent);
    
                manager.notify(0, mBuilder.build());
                Log.d(TAG, "Got new Notification");
            } else {
                // Notification exists. Simply update
            }
        }
    
    }
    

    【讨论】:

    • 我还需要在setNotification 中提供context。这在非活动类 NanoServer 中产生了问题。那么这仍然有效吗?我在 NanoServer 中定义上下文的方式是否正确?
    • 抱歉,我不确定。但我认为没关系,我通常在类似情况下使用应用程序上下文 (getApplicationContext())。
    • 好吧,我遇到了麻烦。在非活动类 Nanoserver 中,你能展示一下你做了什么吗?
    【解决方案2】:

    问题很可能出在您的通知 ID 上,如果您以后想要更新它,它在您的应用程序中必须是唯一的。

    manager.notify(0, builder.build());
    

    将其更改为非零常量

    private static final int NOTE_ID = 2794; //chosen arbitrarily
    
    manager.notify(NOTE_ID, builder.build());
    

    但是,您也不应该持有(泄漏)上下文,而是在使用时获取应用上下文(或服务上下文)。

    为了能够随时获取应用上下文,实现一个应用类(并在清单中注册)

    public class MyApplication extends Application {
    
        public static Context appContext() {
            return this;
        }
    
    }
    
    <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="...">
    
        <!-- The name here should match whatever you called your application class -->
        <application android:name=".MyApplication"
        ...>
            ...
        </application>
    
    </manifest>
    

    【讨论】:

    • 我不确定这是否正确、安全、不会泄漏内存或有效。但我确实设法通过将上下文从服务类传递给 Nanoserver.java 来解决它,在该服务类中初始化服务器。这样我就可以访问其中的上下文。
    • 好的,我确定 - 它正在造成泄漏(以及潜在的崩溃)。更好的选择是使用应用程序类单例,这样您就可以执行 MyApp.getAppContext()。上下文是一个棘手的对象
    • 可以提供样品吗? getAppContext() 在非活动类中不起作用。我已经检查了各种 SO 如何做到这一点。没有运气。那时我想为什么不在启动服务器时将服务上下文本身作为参数传递?
    • 嗯,我没有注意到应用程序崩溃。但唯一不起作用的是前台服务本身。我确实有 30-40 分钟的活动,但在那之后电话会杀死它。出于某种原因,它不尊重唤醒锁,我可以看到这个 - PowerManager: WakeLock finalized while still held
    • 看看我添加的额外代码,这就是你如何制作应用程序的单例
    猜你喜欢
    • 2011-07-28
    • 1970-01-01
    • 1970-01-01
    • 2019-01-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-16
    • 1970-01-01
    相关资源
    最近更新 更多